feat: 实现 PDF 文档阅读功能

- 实现 DocumentEngine,支持 PDF/EPUB/MOBI/TXT/Markdown/代码文件格式
- 添加文档格式自动检测功能
- 实现文档渲染为 HTML
- 实现全文搜索功能
- 添加 CLI/TUI 用户界面
- 修复 tracing-subscriber feature 依赖问题
This commit is contained in:
大麦
2026-03-09 07:55:09 +08:00
parent 28be3b8509
commit 00fa25aeeb
5 changed files with 665 additions and 23 deletions

View File

@@ -1,20 +1,182 @@
//! UI 模块
//!
//! 使用 Dioxus 构建跨平台 UI
//! ReadFlow 用户界面
//!
//! 当前版本使用 CLI/TUI 模式,后续可扩展为桌面 GUI
use crate::config::Config;
use crate::core::document::DocumentEngine;
pub fn run(config: Config) {
println!("Starting UI with config: {:?}", config.theme);
println!("╔══════════════════════════════════════╗");
println!("║ ReadFlow v0.1.0 ║");
println!("║ 面向开发者的文档阅读工具 ║");
println!("╚══════════════════════════════════════╝");
println!();
println!("主题: {}", config.theme.mode);
println!("默认格式: {}", config.reader.default_format);
println!("书库路径: {}", config.storage.library_path);
println!();
// 后续实现Dioxus UI 启动
// 示例:
// dioxus::launch(App);
// 检查命令行参数
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
print_help();
return;
}
let command = &args[1];
match command.as_str() {
"open" => {
if args.len() < 3 {
eprintln!("用法: readflow open <文件路径>");
return;
}
open_document(&args[2]);
}
"search" => {
if args.len() < 4 {
eprintln!("用法: readflow search <文件路径> <关键词>");
return;
}
search_document(&args[2], &args[3]);
}
"info" => {
if args.len() < 3 {
eprintln!("用法: readflow info <文件路径>");
return;
}
show_document_info(&args[2]);
}
"help" | "--help" | "-h" => {
print_help();
}
_ => {
// 尝试直接打开文件
open_document(&args[1]);
}
}
}
// 后续实现Dioxus 组件
// pub fn App(cx: Scope) -> Element {
// cx.render(rsx! {
// div { "Hello, ReadFlow!" }
// })
// }
fn print_help() {
println!("用法:");
println!(" readflow <文件路径> 打开文档");
println!(" readflow open <文件路径> 打开文档");
println!(" readflow info <文件路径> 显示文档信息");
println!(" readflow search <文件> <关键词> 搜索文档内容");
println!();
println!("支持格式: PDF, EPUB, MOBI, TXT, Markdown, 代码文件");
}
fn open_document(path: &str) {
println!("正在打开: {}", path);
println!("{}", "-".repeat(50));
let engine = DocumentEngine::new();
match engine.open(path) {
Ok(doc) => {
println!("✅ 文档打开成功!");
println!();
println!("📖 {}", doc.title);
println!("📄 格式: {:?}", doc.format);
println!("📑 页数: {}", doc.metadata.page_count);
println!("💾 大小: {} bytes", doc.metadata.file_size);
println!();
// 渲染文档内容(简化版)
match engine.render(&doc) {
Ok(html) => {
// 只显示前几行
let preview: String = html.lines().take(20).collect();
println!("预览:\n{}", preview);
}
Err(e) => {
eprintln!("渲染失败: {}", e);
}
}
}
Err(e) => {
eprintln!("❌ 打开失败: {}", e);
}
}
}
fn search_document(path: &str, query: &str) {
println!("{} 中搜索: {}", path, query);
println!("{}", "-".repeat(50));
let engine = DocumentEngine::new();
let doc = match engine.open(path) {
Ok(d) => d,
Err(e) => {
eprintln!("❌ 打开失败: {}", e);
return;
}
};
match engine.search(&doc, query) {
Ok(results) => {
println!("找到 {} 个结果:", results.len());
println!();
for (i, result) in results.iter().take(10).enumerate() {
println!("[{}.] 第 {}", i + 1, result.page);
println!(" 上下文: ...{}...", result.context);
println!();
}
if results.len() > 10 {
println!("... 还有 {} 个结果", results.len() - 10);
}
}
Err(e) => {
eprintln!("❌ 搜索失败: {}", e);
}
}
}
fn show_document_info(path: &str) {
println!("文档信息: {}", path);
println!("{}", "-".repeat(50));
let engine = DocumentEngine::new();
match engine.open(path) {
Ok(doc) => {
println!("标题: {}", doc.title);
println!("路径: {}", doc.path);
println!("格式: {:?}", doc.format);
println!();
println!("元数据:");
println!(" 页数: {}", doc.metadata.page_count);
println!(" 文件大小: {} bytes", doc.metadata.file_size);
println!(" 作者: {:?}", doc.metadata.author);
println!();
// 获取目录
match engine.get_toc(&doc) {
Ok(toc) => {
if toc.is_empty() {
println!("目录: (无)");
} else {
println!("目录:");
for entry in toc {
let indent = " ".repeat(entry.level);
println!("{}{}. {}", indent, entry.page, entry.title);
}
}
}
Err(e) => {
println!("获取目录失败: {}", e);
}
}
}
Err(e) => {
eprintln!("❌ 获取信息失败: {}", e);
}
}
}