#![cfg_attr(not(feature = "std"), no_std)]
#![deny(unsafe_code)]
#[cfg(not(feature = "std"))]
extern crate alloc;
pub mod iff;
pub mod error;
pub(crate) mod info;
pub(crate) use djvu_zp as zp_impl;
#[allow(dead_code)]
pub mod bzz_new;
#[cfg(feature = "std")]
pub mod bzz_encode;
#[cfg(feature = "std")]
pub mod djvm;
pub mod jb2;
#[path = "iw44_new.rs"]
pub mod iw44_new;
#[cfg(feature = "std")]
pub mod iw44_encode;
#[cfg(feature = "std")]
pub mod jb2_encode;
#[cfg(feature = "std")]
pub mod fgbz_encode;
#[cfg(feature = "std")]
pub mod djvu_encode;
#[cfg(feature = "std")]
pub mod segment;
pub mod djvu_document;
#[cfg(feature = "std")]
pub mod djvu_mut;
pub mod djvu_render;
pub mod text;
pub mod annotation;
pub mod metadata;
#[cfg(feature = "std")]
pub mod pdf;
#[cfg(feature = "epub")]
pub mod epub;
#[cfg(feature = "tiff")]
pub mod tiff_export;
#[cfg(feature = "async")]
pub mod djvu_async;
#[cfg(feature = "image")]
pub mod image_compat;
#[cfg(feature = "std")]
pub mod ocr_export;
#[cfg(feature = "std")]
pub mod ocr;
#[cfg(feature = "ocr-tesseract")]
pub mod ocr_tesseract;
#[cfg(feature = "ocr-onnx")]
pub mod ocr_onnx;
#[cfg(feature = "ocr-neural")]
pub mod ocr_neural;
#[cfg(feature = "std")]
pub mod text_encode;
#[cfg(feature = "std")]
pub mod navm_encode;
pub mod smmr;
#[cfg(feature = "wasm")]
pub mod wasm;
#[cfg(feature = "std")]
#[allow(unsafe_code)]
pub mod ffi;
pub use error::{BzzError, DjVuError, IffError, Iw44Error, Jb2Error};
pub use djvu_document::{DjVuBookmark, DjVuDocument, DjVuPage, DocError};
pub use info::{PageInfo, Rotation};
#[doc(hidden)]
pub(crate) mod bitmap;
#[doc(hidden)]
pub(crate) mod pixmap;
pub use bitmap::Bitmap;
pub use pixmap::{GrayPixmap, Pixmap};
#[cfg(feature = "std")]
pub use text::{TextLayer, TextZone, TextZoneKind};
#[cfg(feature = "std")]
pub type Bookmark = DjVuBookmark;
#[cfg(feature = "std")]
pub use error::LegacyError as Error;
#[cfg(feature = "std")]
pub struct Document {
doc: DjVuDocument,
}
#[cfg(feature = "std")]
impl Document {
pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, Error> {
let data = std::fs::read(path.as_ref())
.map_err(|e| Error::FormatError(format!("failed to read file: {}", e)))?;
Self::from_bytes(data)
}
pub fn open_dir(path: impl AsRef<std::path::Path>) -> Result<Self, Error> {
let path = path.as_ref();
let data = std::fs::read(path)
.map_err(|e| Error::FormatError(format!("failed to read file: {}", e)))?;
let base_dir = path.parent().unwrap_or(std::path::Path::new("."));
let doc = DjVuDocument::parse_from_dir(&data, base_dir)
.map_err(|e| Error::FormatError(e.to_string()))?;
Ok(Document { doc })
}
pub fn from_reader(reader: impl std::io::Read) -> Result<Self, Error> {
let mut reader = reader;
let mut data = Vec::new();
reader
.read_to_end(&mut data)
.map_err(|e| Error::FormatError(format!("failed to read: {}", e)))?;
Self::from_bytes(data)
}
pub fn from_bytes(data: Vec<u8>) -> Result<Self, Error> {
let doc = DjVuDocument::parse(&data).map_err(|e| Error::FormatError(e.to_string()))?;
Ok(Document { doc })
}
pub fn bookmarks(&self) -> Result<Vec<Bookmark>, Error> {
Ok(self.doc.bookmarks().to_vec())
}
pub fn page_count(&self) -> usize {
self.doc.page_count()
}
pub fn page(&self, index: usize) -> Result<Page<'_>, Error> {
let page = self
.doc
.page(index)
.map_err(|e| Error::FormatError(e.to_string()))?;
Ok(Page { page, index })
}
}
#[cfg(feature = "std")]
pub struct Page<'a> {
page: &'a DjVuPage,
index: usize,
}
#[cfg(feature = "std")]
impl<'a> Page<'a> {
pub fn width(&self) -> u32 {
self.page.width() as u32
}
pub fn height(&self) -> u32 {
self.page.height() as u32
}
pub fn display_width(&self) -> u32 {
self.display_dims().0
}
pub fn display_height(&self) -> u32 {
self.display_dims().1
}
fn display_dims(&self) -> (u32, u32) {
let w = self.page.width() as u32;
let h = self.page.height() as u32;
match self.page.rotation() {
info::Rotation::Cw90 | info::Rotation::Ccw90 => (h, w),
_ => (w, h),
}
}
pub fn dpi(&self) -> u16 {
self.page.dpi()
}
pub fn index(&self) -> usize {
self.index
}
pub fn rotation(&self) -> info::Rotation {
self.page.rotation()
}
fn render_err(e: djvu_render::RenderError) -> Error {
Error::FormatError(e.to_string())
}
pub fn decode_mask(&self) -> Result<Option<Bitmap>, Error> {
self.page
.extract_mask()
.map_err(|e| Error::FormatError(e.to_string()))
}
pub fn render(&self) -> Result<Pixmap, Error> {
let (w, h) = self.display_dims();
let opts = djvu_render::RenderOptions {
width: w,
height: h,
scale: 1.0,
..Default::default()
};
djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)
}
pub fn render_to_size(&self, width: u32, height: u32) -> Result<Pixmap, Error> {
let (dw, dh) = self.display_dims();
let scale = if dw > 0 {
width as f32 / dw as f32
} else {
1.0
};
let _ = dh;
let opts = djvu_render::RenderOptions {
width,
height,
scale,
..Default::default()
};
djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)
}
pub fn render_bold(&self, dilate_passes: u32) -> Result<Pixmap, Error> {
let (w, h) = self.display_dims();
let opts = djvu_render::RenderOptions {
width: w,
height: h,
scale: 1.0,
bold: dilate_passes.min(255) as u8,
..Default::default()
};
djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)
}
pub fn render_to_size_bold(
&self,
width: u32,
height: u32,
dilate_passes: u32,
) -> Result<Pixmap, Error> {
let (dw, _dh) = self.display_dims();
let scale = if dw > 0 {
width as f32 / dw as f32
} else {
1.0
};
let opts = djvu_render::RenderOptions {
width,
height,
scale,
bold: dilate_passes.min(255) as u8,
..Default::default()
};
djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)
}
pub fn render_aa(&self, width: u32, height: u32, _boldness: f32) -> Result<Pixmap, Error> {
let (dw, _dh) = self.display_dims();
let scale = if dw > 0 {
width as f32 / dw as f32
} else {
1.0
};
let opts = djvu_render::RenderOptions {
width,
height,
scale,
aa: true,
..Default::default()
};
djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)
}
pub fn thumbnail(&self) -> Result<Option<Pixmap>, Error> {
self.page
.thumbnail()
.map_err(|e| Error::FormatError(e.to_string()))
}
pub fn text_layer(&self) -> Result<Option<TextLayer>, Error> {
self.page
.text_layer()
.map_err(|e| Error::FormatError(e.to_string()))
}
pub fn text(&self) -> Result<Option<String>, Error> {
Ok(self.text_layer()?.map(|tl| tl.text))
}
pub fn render_scaled_coarse(&self, scale: f32) -> Result<Option<Pixmap>, Error> {
let (dw, dh) = self.display_dims();
let w = ((dw as f32 * scale).round() as u32).max(1);
let h = ((dh as f32 * scale).round() as u32).max(1);
let opts = djvu_render::RenderOptions {
width: w,
height: h,
scale,
..Default::default()
};
djvu_render::render_coarse(self.page, &opts).map_err(Self::render_err)
}
pub fn render_scaled_progressive(&self, scale: f32) -> Result<Vec<Pixmap>, Error> {
let (dw, dh) = self.display_dims();
let w = ((dw as f32 * scale).round() as u32).max(1);
let h = ((dh as f32 * scale).round() as u32).max(1);
let opts = djvu_render::RenderOptions {
width: w,
height: h,
scale,
..Default::default()
};
let n_bg44 = self.page.bg44_chunks().len();
if n_bg44 == 0 {
let pixmap = djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)?;
return Ok(vec![pixmap]);
}
let mut result = Vec::with_capacity(n_bg44);
for chunk_n in 0..n_bg44 {
let pixmap = djvu_render::render_progressive(self.page, &opts, chunk_n)
.map_err(Self::render_err)?;
result.push(pixmap);
}
Ok(result)
}
pub fn render_scaled(&self, scale: f32) -> Result<Pixmap, Error> {
let (dw, dh) = self.display_dims();
let w = ((dw as f32 * scale).round() as u32).max(1);
let h = ((dh as f32 * scale).round() as u32).max(1);
let opts = djvu_render::RenderOptions {
width: w,
height: h,
scale,
..Default::default()
};
djvu_render::render_pixmap(self.page, &opts).map_err(Self::render_err)
}
}
#[cfg(feature = "std")]
#[allow(dead_code)]
const _: () = {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
fn assertions() {
assert_send::<Document>();
assert_sync::<Document>();
}
};