From 1c94f157fe250e880a40db7f2711a00dc0975368 Mon Sep 17 00:00:00 2001 From: Ding Date: Mon, 17 Nov 2025 09:49:11 +0800 Subject: [PATCH 1/4] impl drop --- lib/src/epub/reader.rs | 14 +++++--------- lib/src/mobi/reader.rs | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/src/epub/reader.rs b/lib/src/epub/reader.rs index 6d02bf1..09e9ef6 100644 --- a/lib/src/epub/reader.rs +++ b/lib/src/epub/reader.rs @@ -708,25 +708,21 @@ fn has_epub_type(e: &BytesStart, value: &str) -> bool { } #[derive(Debug, Clone)] -struct EpubReader { +struct EpubReader { inner: zip::ZipArchive, } -// impl From> for EpubReader { -// fn from(value: Vec) -> Self { - -// EpubReader{ -// inner: -// } +impl Drop for EpubReader { + fn drop(&mut self) {} +} -// } -// } impl EpubReader { pub fn new(value: T) -> IResult { let r = zip::ZipArchive::new(value)?; Ok(EpubReader { inner: r }) } } + impl EpubReaderTrait for EpubReader { fn read(&mut self, book: &mut EpubBook) -> IResult<()> { let reader = &mut self.inner; diff --git a/lib/src/mobi/reader.rs b/lib/src/mobi/reader.rs index 50cf748..30bc5ff 100644 --- a/lib/src/mobi/reader.rs +++ b/lib/src/mobi/reader.rs @@ -394,7 +394,7 @@ where Ok(true) } -pub struct MobiReader { +pub struct MobiReader { reader: BufReader, pub(crate) pdb_header: PDBHeader, pub(crate) mobi_doc_header: MOBIDOCHeader, @@ -406,6 +406,13 @@ pub struct MobiReader { id: AtomicUsize, } +impl Drop for MobiReader { + fn drop(&mut self) { + self.release_memory(); + self.exth_header = None; + } +} + impl MobiReader { pub fn new(v: T) -> IResult> { // let fs = std::fs::File::open(file)?; @@ -649,6 +656,15 @@ impl MobiReader { Ok(pos) } + + pub fn release_memory(&mut self) { + if let Some(mut cache) = self.text_cache.take() { + // 显式清空 Vec,释放内存 + cache.clear(); + // Vec 会在离开作用域时自动释放,但 clear() 可以立即释放容量 + cache.shrink_to_fit(); + } + } } /// 文本分节 From b291ef89115bc180f249a87bee101adac4f0f145 Mon Sep 17 00:00:00 2001 From: Ding Date: Mon, 17 Nov 2025 10:24:37 +0800 Subject: [PATCH 2/4] Update core.rs --- lib/src/epub/core.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/src/epub/core.rs b/lib/src/epub/core.rs index c625bd3..93a3fab 100644 --- a/lib/src/epub/core.rs +++ b/lib/src/epub/core.rs @@ -605,6 +605,11 @@ impl Display for EpubBook { ) } } +impl Drop for EpubBook { + fn drop(&mut self) { + self.release_memory(); + } +} impl EpubBook { iepub_derive::option_string_method!(info, creator); @@ -744,7 +749,7 @@ impl EpubBook { self.assets.iter_mut() } - pub fn remove_assets(&mut self,index: usize){ + pub fn remove_assets(&mut self, index: usize) { self.assets.remove(index); } @@ -763,7 +768,7 @@ impl EpubBook { self.chapters.iter() } - pub fn remove_chapter(&mut self, index: usize){ + pub fn remove_chapter(&mut self, index: usize) { self.chapters.remove(index); } @@ -860,6 +865,16 @@ impl EpubBook { } } + pub fn release_memory(&mut self) { + self.reader = None; + self.chapters.clear(); + self.assets.clear(); + self.nav.clear(); + self.meta.clear(); + self.cover = None; + self.version = String::new(); + } + #[cfg(feature = "cache")] pub fn cache>(&self, file: T) -> IResult<()> { std::fs::write(file, serde_json::to_string(self).unwrap())?; From 7008765c2e7d37c7ec088359a7c7b8f4a86e229e Mon Sep 17 00:00:00 2001 From: "michael.ding" Date: Thu, 8 Jan 2026 14:54:59 +0800 Subject: [PATCH 3/4] upgrade dependencies --- lib/Cargo.toml | 13 +++++++------ lib/src/adapter/core.rs | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 695d503..141d555 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -8,20 +8,21 @@ repository = { workspace = true } readme = { workspace = true } keywords = { workspace = true } rust-version = { workspace = true } + [dependencies] -zip = "6.0.0" +zip = "7.0.0" anyhow = "1.0.100" -quick-xml = { version = "0.38.3" } +quick-xml = { version = "0.38.4" } ab_glyph = { version = "0.2.32", optional = true } -imageproc = { version = "0.25.0", optional = true} -serde_json = { version = "1.0.145", optional = true } +imageproc = { version = "0.26.0", optional = true} +serde_json = { version = "1.0.149", optional = true } iepub-derive = { path = "../derive", version = "1.2.6" } serde = { version = "1.0.228", features = ["derive"], optional = true } -image = { version = "0.25.8", default-features = false, features = ["jpeg"], optional = true } +image = { version = "0.25.9", default-features = false, features = ["jpeg"], optional = true } md-5 = {version = "0.10.6", optional = true } [dev-dependencies] -reqwest = { version = "0.11", features = ["blocking"] } +reqwest = { version = "0.13.1", features = ["blocking"] } [features] no_nav=[] diff --git a/lib/src/adapter/core.rs b/lib/src/adapter/core.rs index 2ae5b10..8019507 100644 --- a/lib/src/adapter/core.rs +++ b/lib/src/adapter/core.rs @@ -674,7 +674,7 @@ pub mod concat { .with_title("1.1") .with_file_name("1/1.xhtml"), ); - let mut builder = EpubBuilder::new() + let builder = EpubBuilder::new() .custome_nav(true) .with_title("测试合并") .with_creator("作者") @@ -706,7 +706,7 @@ pub mod concat { .with_title("3.1") .with_file_name("2/1.xhtml"), ); - let mut builder = EpubBuilder::new() + let builder = EpubBuilder::new() .custome_nav(true) .with_title("测试合并2") .with_creator("作者2") @@ -730,11 +730,11 @@ pub mod concat { ); let mut book2 = builder.book().unwrap(); - let mut builder = EpubBuilder::default(); - let (mut builder, len, a_len) = + let builder = EpubBuilder::default(); + let (builder, len, a_len) = add_into_epub(builder, &mut book1, 0, 0, 0, None, &[]).unwrap(); - let (mut builder, len, a_len) = + let (builder, len, a_len) = add_into_epub(builder, &mut book2, len, a_len, 0, None, &[]).unwrap(); let b = builder.book().unwrap(); From 22516837d669387d434955d37f04965cfa02282c Mon Sep 17 00:00:00 2001 From: "michael.ding" Date: Fri, 9 Jan 2026 11:20:04 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BB=8Ehtml=E4=B8=AD=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=87=BA=E6=A0=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/epub/core.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++ lib/src/epub/html.rs | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/src/epub/core.rs b/lib/src/epub/core.rs index c1b3ab6..dbd597e 100644 --- a/lib/src/epub/core.rs +++ b/lib/src/epub/core.rs @@ -3,6 +3,9 @@ use std::fmt::{Debug, Display}; use std::io::Write; use std::sync::{Arc, Mutex}; +use quick_xml::events::Event; +use quick_xml::Reader; + use super::common::{self}; use super::html::{get_html_info, to_html}; use crate::cache_struct; @@ -137,6 +140,17 @@ crate::cache_struct! { } } +crate::cache_struct! { + /** + * 标注 + */ + #[derive(Debug, Clone)] + pub struct Annotation{ + id:String, + text:String, + } +} + epub_base_field! { #[derive(Default, Clone)] pub struct EpubHtml { @@ -152,6 +166,7 @@ epub_base_field! { pub(crate) direction: Option, /// body 标签上的attribute pub(crate) body_attribute: Option>, + pub(crate) annotations: Option>, } } @@ -183,6 +198,78 @@ impl EpubHtml { self._data.as_deref() } + fn get_annotation(&mut self, data: &Vec) -> Vec { + let mut annotations = Vec::new(); + if let Ok(content) = String::from_utf8(data.to_vec()) { + let mut reader = quick_xml::reader::NsReader::from_str(&content); + reader.config_mut().trim_text(true); + let mut buf = Vec::new(); + let mut in_annotation = false; + let mut current_text = String::new(); + let mut data_id: Option = None; + loop { + if let Ok(event) = reader.read_event_into(&mut buf) { + match event { + Event::Start(e) => { + // 检测标注标签(如 ) + if e.name().as_ref() == b"span" { + if let Some(attr) = e.attributes().find(|a| { + a.as_ref() + .map(|a| a.key == quick_xml::name::QName(b"class")) + .unwrap_or(false) + }) { + if let Ok(attr) = attr { + let class_val = attr.unescape_value().unwrap_or_default(); + if class_val.contains("annotation") { + in_annotation = true; + // 提取元数据标识(如 data-id) + data_id = e + .attributes() + .find(|a| { + a.as_ref() + .map(|a| { + a.key + == quick_xml::name::QName( + b"data-id", + ) + }) + .unwrap_or(false) + }) + .and_then(|a| { + a.ok().map(|a| { + a.unescape_value() + .unwrap_or_default() + .to_string() + }) + }); + } + } + } + } + } + Event::Text(e) if in_annotation => { + current_text.push_str(&e.decode().unwrap_or_default()); + // 累积标注文本 + } + Event::End(e) if e.name().as_ref() == b"span" && in_annotation => { + if let Some(id) = data_id.take() { + annotations.push(Annotation { + id, + text: current_text.clone(), + }); + } + in_annotation = false; + } + Event::Eof => break, + _ => {} + } + } + buf.clear(); + } + } + annotations + } + pub(crate) fn read_data(&mut self, reader: &mut impl EpubReaderTrait) { let (id, origin) = if let Some(index) = self._file_name.find('#') { ( @@ -214,6 +301,7 @@ impl EpubHtml { if !title.is_empty() { self.set_title(&title); } + self.get_annotation(&content); self.set_data(content); if let Some(lang) = language { self.set_language(lang); @@ -273,6 +361,7 @@ impl EpubHtml { if !title.is_empty() { self.set_title(&title); } + self.get_annotation(&content); self.set_data(content); if let Some(lang) = language { self.set_language(lang); @@ -303,6 +392,10 @@ impl EpubHtml { self._data = None; } + pub fn annotation(&self) -> Option> { + self.annotations.clone() + } + pub fn format(&mut self) -> Option { self.data_mut(); Some(to_html(self, false, &None)) diff --git a/lib/src/epub/html.rs b/lib/src/epub/html.rs index 23be9d0..618bf62 100644 --- a/lib/src/epub/html.rs +++ b/lib/src/epub/html.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use super::common; -use crate::{common::get_media_type, prelude::*}; +use crate::{common::get_media_type, epub::core::Annotation, prelude::*}; use quick_xml::events::Event; /// 生成html