epub_builder/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with
3// this file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! A library to generate EPUB files.
6//!
7//! The purpose for this library is to make it easier to generate EPUB files:
8//! it should take care of most of the boilerplate for you, leaving you only
9//! with the task of filling the actual content.
10//!
11//! # Usage
12//!
13//! Add this in your `Cargo.toml` file:
14//!
15//! ```toml
16//! [dependencies]
17//! epub-builder = "0.8"
18//! ```
19//!
20//! # Example
21//! ```rust
22//! use epub_builder::EpubBuilder;
23//! use epub_builder::EpubContent;
24//! use epub_builder::ReferenceType;
25//! use epub_builder::Result;
26//! use epub_builder::TocElement;
27//! use epub_builder::ZipLibrary;
28//!
29//! use std::io;
30//! use std::io::Write;
31//!
32//! // Try to print Zip file to stdout
33//! fn run() -> Result<()> {
34//! env_logger::init();
35//! // Some dummy content to fill our book
36//! let dummy_content = r#"<?xml version="1.0" encoding="UTF-8"?>
37//! //! <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
38//! <body>
39//! <p>Text of the page</p>
40//! </body>
41//! </html>"#;
42//! let dummy_image = "Not really a PNG image";
43//! let dummy_css = "body { background-color: pink }";
44//!
45//! // Create a new EpubBuilder using the zip library
46//! let mut builder = EpubBuilder::new(ZipLibrary::new()?)?;
47//! // Set some metadata
48//! builder
49//! .metadata("author", "Joan Doe")?
50//! .metadata("title", "Dummy Book <T>")?
51//! // Set the stylesheet (create a "stylesheet.css" file in EPUB that is used by some generated files)
52//! .stylesheet(dummy_css.as_bytes())?
53//! // Add a image cover file
54//! .add_cover_image("cover.png", dummy_image.as_bytes(), "image/png")?
55//! // Add a resource that is not part of the linear document structure
56//! .add_resource("some_image.png", dummy_image.as_bytes(), "image/png")?
57//! // Add a cover page
58//! .add_content(
59//! EpubContent::new("cover.xhtml", dummy_content.as_bytes())
60//! .title("Cover")
61//! .reftype(ReferenceType::Cover),
62//! )?
63//! // Add a title page
64//! .add_content(
65//! EpubContent::new("title.xhtml", dummy_content.as_bytes())
66//! .title("Title <T>")
67//! .reftype(ReferenceType::TitlePage),
68//! )?
69//! // Add a chapter, mark it as beginning of the "real content"
70//! .add_content(
71//! EpubContent::new("chapter_1.xhtml", dummy_content.as_bytes())
72//! .title("Chapter 1 <T>")
73//! .reftype(ReferenceType::Text),
74//! )?
75//! // Add a second chapter; this one has more toc information about its internal structure
76//! .add_content(
77//! EpubContent::new("chapter_2.xhtml", dummy_content.as_bytes())
78//! .title("Chapter 2 <T>")
79//! .child(TocElement::new("chapter_2.xhtml#1", "Chapter 2, section 1")),
80//! )?
81//! // Add a section. Since its level is set to 2, it will be attached to the previous chapter.
82//! .add_content(
83//! EpubContent::new("section.xhtml", dummy_content.as_bytes())
84//! .title("Chapter 2 <T>, section 2")
85//! .level(2),
86//! )?
87//! // Add a chapter without a title, which will thus not appear in the TOC.
88//! .add_content(EpubContent::new("notes.xhtml", dummy_content.as_bytes()))?
89//! // Generate a toc inside of the document, that will be part of the linear structure.
90//! .inline_toc();
91//! // Finally, write the EPUB file to stdout
92//! builder.generate(&mut io::stdout())?; // generate into stout
93//!
94//! log::debug!("dummy book generation is done");
95//! Ok(())
96//! }
97//!
98//! fn main() {
99//! match run() {
100//! Ok(_) => writeln!(
101//! &mut io::stderr(),
102//! "Successfully wrote epub document to stdout!"
103//! )
104//! .unwrap(),
105//! Err(err) => writeln!(&mut io::stderr(), "Error: {}", err).unwrap(),
106//! };
107//! }
108//! ```
109//!
110//! # Features
111//!
112//! `epub-builder`'s aim is to make EPUB generation simpler. It takes care of zipping
113//! the files and generate the following ones:
114//!
115//! * `mimetype`
116//! * `toc.ncx`
117//! * `nav.xhtml`
118//! * `manifest.xml`
119//! * `content.opf`
120//! * `com.apple.ibooks.display-options.xml`.
121//!
122//! It also tries to make it easier to have a correct table of contents, and optionally
123//! generate an inline one in the document.
124//!
125//! Supported EPUB versions:
126//!
127//! * 2.0.1 (default)
128//! * 3.0.1
129//!
130//! ## Missing features
131//!
132//! There are various EPUB features that `epub-builder` doesn't handle. Particularly,
133//! there are some metadata that could be better
134//! handled (e.g. support multiple authors, multiple languages in the document and so on).
135//!
136//! There are also various things that aren't in the scope of this library: it doesn't
137//! provide a default CSS, templates for your XHTML content and so on. This is left to
138//! libraries or applications using it.
139//!
140//! # Conditional compilation
141//!
142//! EPUB files are Zip files, so we need to zip. By default, this library provides
143//! wrappers around both the [Rust zip library](https://crates.io/crates/zip) and calls
144//! to the `zip` command that may (or may not) be installed on your system.
145//!
146//! It is possible to disable the compilation (and the dependencies) of either of these
147//! wrappers, using `no-default-features`. (If you don't enable at least one of them this
148//! library will be pretty useless).
149//!
150//! # License
151//!
152//! This is free software, published under the [Mozilla Public License,
153//! version 2.0](https://www.mozilla.org/en-US/MPL/2.0/).
154#![deny(missing_docs)]
155
156mod common;
157mod epub;
158mod epub_content;
159mod templates;
160mod toc;
161mod zip;
162#[cfg(feature = "zip-command")]
163mod zip_command;
164#[cfg(feature = "zip-command")]
165#[cfg(feature = "libzip")]
166mod zip_command_or_library;
167#[cfg(feature = "libzip")]
168mod zip_library;
169
170pub use epub::EpubBuilder;
171pub use epub::EpubVersion;
172pub use epub::MetadataOpf;
173pub use epub::MetadataOpfV3;
174pub use epub::PageDirection;
175pub use epub_content::EpubContent;
176pub use epub_content::ReferenceType;
177use libzip::result::ZipError;
178pub use toc::Toc;
179pub use toc::TocElement;
180#[cfg(feature = "zip-command")]
181pub use zip_command::ZipCommand;
182#[cfg(feature = "zip-command")]
183#[cfg(feature = "libzip")]
184pub use zip_command_or_library::ZipCommandOrLibrary;
185#[cfg(feature = "libzip")]
186pub use zip_library::ZipLibrary;
187
188/// Error type of this crate. Each variant represent a type of event that may happen during this crate's operations.
189#[derive(thiserror::Error, Debug)]
190pub enum Error {
191 /// An error caused while processing a template or its rendering.
192 #[error("{msg}: {cause:?}")]
193 TemplateError {
194 /// A message explaining what was happening when we recieved this error.
195 msg: String,
196 /// The root cause of the error.
197 // Box the error, since it is quite large (at least 136 bytes, thanks clippy!)
198 cause: Box<upon::Error>,
199 },
200 /// An error returned when encountering an unknown [`PageDirection`].
201 #[error("Invalid page direction specification: {0}")]
202 PageDirectionError(String),
203 /// An error returned when an unknown metadata key has been encountered.
204 #[error("Invalid metadata key: {0}")]
205 InvalidMetadataError(String),
206 /// An error returned when attempting to access the filesystem
207 #[error("{msg}: {cause:?}")]
208 IoError {
209 /// A message explaining what was happening when we recieved this error.
210 msg: String,
211 /// The root cause of the error.
212 cause: std::io::Error,
213 },
214 /// An error returned when something happened while invoking a zip program. See [`ZipCommand`].
215 #[error("Error while executing zip command: {0}")]
216 ZipCommandError(String),
217 /// An error returned when the zip library itself returned an error. See [`ZipLibrary`].
218 #[error(transparent)]
219 ZipError(#[from] ZipError),
220 /// An error returned when the zip library itself returned an error, but with an additional message. See [`ZipLibrary`].
221 #[error("{msg}: {cause:?}")]
222 ZipErrorWithMessage {
223 /// A message explaining what was happening when we recieved this error.
224 msg: String,
225 /// The root cause of the error.
226 cause: ZipError,
227 },
228 /// An error returned when an invalid [`Path`] has been encountered during epub processing.
229 #[error("Invalid path: {0}")]
230 InvalidPath(String),
231}
232
233impl From<std::io::Error> for Error {
234 fn from(value: std::io::Error) -> Self {
235 Error::IoError {
236 msg: format!("{value:?}"),
237 cause: value,
238 }
239 }
240}
241
242/// A more convenient shorthand for functions returning an error in this crate.
243pub type Result<T> = std::result::Result<T, Error>;