fix title extraction and epub errors
This commit is contained in:
+52
-9
@@ -3,6 +3,7 @@ use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use chrono::Utc;
|
||||
use quick_xml::escape::escape;
|
||||
use zip::CompressionMethod;
|
||||
use zip::write::{SimpleFileOptions, ZipWriter};
|
||||
@@ -181,11 +182,16 @@ fn build_ncx(manifest: &crate::manifest::Manifest, built: &[BuiltEntry]) -> Stri
|
||||
let section_play_order = play_order;
|
||||
play_order += 1;
|
||||
|
||||
let section_target = section_entries[0]
|
||||
.section_anchor
|
||||
.as_ref()
|
||||
.map(|anchor| format!("text/{}.xhtml#{}", section_entries[0].id, anchor))
|
||||
.unwrap_or_else(|| format!("text/{}.xhtml", section_entries[0].id));
|
||||
let mut child_points = String::new();
|
||||
for entry in §ion_entries {
|
||||
child_points.push_str(&format!(
|
||||
"<navPoint id=\"nav-{}\" playOrder=\"{}\"><navLabel><text>{}</text></navLabel><content src=\"text/{}.xhtml\"/></navPoint>",
|
||||
escape(&entry.id),
|
||||
escape(&xml_id("nav", &entry.id)),
|
||||
play_order,
|
||||
escape(&entry.chapter.nav_title),
|
||||
escape(&entry.id)
|
||||
@@ -194,11 +200,11 @@ fn build_ncx(manifest: &crate::manifest::Manifest, built: &[BuiltEntry]) -> Stri
|
||||
}
|
||||
|
||||
nav_points.push_str(&format!(
|
||||
"<navPoint id=\"section-{}\" playOrder=\"{}\"><navLabel><text>{}</text></navLabel><content src=\"text/{}.xhtml\"/>{}</navPoint>",
|
||||
escape(§ion.id),
|
||||
"<navPoint id=\"{}\" playOrder=\"{}\"><navLabel><text>{}</text></navLabel><content src=\"{}\"/>{}</navPoint>",
|
||||
escape(&xml_id("section", §ion.id)),
|
||||
section_play_order,
|
||||
escape(§ion.title),
|
||||
escape(§ion_entries[0].id),
|
||||
escape(§ion_target),
|
||||
child_points
|
||||
));
|
||||
}
|
||||
@@ -233,14 +239,17 @@ fn build_opf(
|
||||
for entry in built {
|
||||
manifest_items.push_str(&format!(
|
||||
"<item id=\"{}\" href=\"text/{}.xhtml\" media-type=\"application/xhtml+xml\"/>",
|
||||
escape(&entry.id),
|
||||
escape(&xml_id("entry", &entry.id)),
|
||||
escape(&entry.id)
|
||||
));
|
||||
spine_items.push_str(&format!("<itemref idref=\"{}\"/>", escape(&entry.id)));
|
||||
spine_items.push_str(&format!(
|
||||
"<itemref idref=\"{}\"/>",
|
||||
escape(&xml_id("entry", &entry.id))
|
||||
));
|
||||
for asset in &entry.assets {
|
||||
manifest_items.push_str(&format!(
|
||||
"<item id=\"{}\" href=\"{}\" media-type=\"{}\"/>",
|
||||
escape(&asset.id),
|
||||
escape(&xml_id("asset", &asset.id)),
|
||||
escape(&asset.href),
|
||||
escape(&asset.media_type)
|
||||
));
|
||||
@@ -249,8 +258,9 @@ fn build_opf(
|
||||
|
||||
if let Some(cover_href) = cover_href {
|
||||
manifest_items.push_str(&format!(
|
||||
"<item id=\"cover\" href=\"{}\" media-type=\"image/jpeg\" properties=\"cover-image\"/>",
|
||||
escape(cover_href)
|
||||
"<item id=\"cover\" href=\"{}\" media-type=\"{}\" properties=\"cover-image\"/>",
|
||||
escape(cover_href),
|
||||
escape(&media_type_from_href(cover_href))
|
||||
));
|
||||
}
|
||||
|
||||
@@ -260,6 +270,7 @@ fn build_opf(
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
let description = manifest.book.description.clone().unwrap_or_default();
|
||||
let modified = Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string();
|
||||
format!(
|
||||
r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package version="3.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="bookid">
|
||||
@@ -269,6 +280,7 @@ fn build_opf(
|
||||
<dc:creator>{}</dc:creator>
|
||||
<dc:language>{}</dc:language>
|
||||
<dc:description>{}</dc:description>
|
||||
<meta property="dcterms:modified">{}</meta>
|
||||
</metadata>
|
||||
<manifest>{}</manifest>
|
||||
<spine toc="ncx">{}</spine>
|
||||
@@ -278,11 +290,42 @@ fn build_opf(
|
||||
escape(&author),
|
||||
escape(&manifest.book.language),
|
||||
escape(&description),
|
||||
escape(&modified),
|
||||
manifest_items,
|
||||
spine_items
|
||||
)
|
||||
}
|
||||
|
||||
fn xml_id(prefix: &str, value: &str) -> String {
|
||||
let mut id = String::with_capacity(prefix.len() + value.len() + 1);
|
||||
id.push_str(prefix);
|
||||
id.push('-');
|
||||
for ch in value.chars() {
|
||||
if ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-' | '.') {
|
||||
id.push(ch);
|
||||
} else {
|
||||
id.push('-');
|
||||
}
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
fn media_type_from_href(href: &str) -> String {
|
||||
match href.rsplit('.').next().map(|ext| ext.to_ascii_lowercase()) {
|
||||
Some(extension) => match extension.as_str() {
|
||||
"jpg" | "jpeg" => "image/jpeg",
|
||||
"png" => "image/png",
|
||||
"gif" => "image/gif",
|
||||
"svg" => "image/svg+xml",
|
||||
"webp" => "image/webp",
|
||||
"avif" => "image/avif",
|
||||
_ => "application/octet-stream",
|
||||
}
|
||||
.to_string(),
|
||||
None => "application/octet-stream".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
const CONTAINER_XML: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
|
||||
Reference in New Issue
Block a user