mod bcmap;
mod cmap;
mod content;
mod crypto;
mod data;
mod file;
mod filters;
mod font;
mod lexer;
mod object;
mod pages;
mod parser;
mod xref;
use serde::Serialize;
use crate::error::Result;
use crate::extract::{
EdgePart, ExtractOptions, GlyphPart, GraphicPart, PagePart, ReaderSettings, extract_from_parts,
};
use crate::model::Document;
use crate::text::{TextChar, TextDoc, TextFont, TextPage};
use content::{ContentCtx, extract_parts, extract_parts_and_text, extract_text};
use pages::{collect_pages, decode_contents, view_height, view_width};
use xref::XRef;
pub fn extract_from_bytes(
bytes: &[u8],
password: Option<&str>,
options: &ExtractOptions,
) -> Result<Document> {
let xref = XRef::parse_with_limit(bytes, password, options.reader.max_decoded_bytes, options.reader.max_cmap_entries)?;
let page_infos = collect_pages(bytes, &xref)?;
let pages = crate::par::map_ordered(
page_infos,
|| &xref,
|xref, _, p| build_page_part(p, bytes, xref),
)
.into_iter()
.collect::<Result<Vec<_>>>()?;
let mut doc = extract_from_parts(pages, options);
doc.warnings = xref.take_warnings();
Ok(doc)
}
pub fn extract_text_from_bytes(bytes: &[u8], password: Option<&str>) -> Result<TextDoc> {
extract_text_from_bytes_with(bytes, password, &ReaderSettings::default())
}
pub fn extract_text_from_bytes_with(
bytes: &[u8],
password: Option<&str>,
reader: &ReaderSettings,
) -> Result<TextDoc> {
let xref = XRef::parse_with_limit(bytes, password, reader.max_decoded_bytes, reader.max_cmap_entries)?;
let page_infos = collect_pages(bytes, &xref)?;
let pages = crate::par::map_ordered(
page_infos,
|| &xref,
|xref, _, p| build_text_page(p, bytes, xref),
)
.into_iter()
.collect::<Result<Vec<_>>>()?;
Ok(TextDoc {
pages,
warnings: xref.take_warnings(),
})
}
pub fn read_pages(bytes: &[u8], password: Option<&str>) -> Result<Vec<PagePart>> {
read_pages_with(bytes, password, &ReaderSettings::default())
}
pub fn read_pages_with(
bytes: &[u8],
password: Option<&str>,
reader: &ReaderSettings,
) -> Result<Vec<PagePart>> {
let xref = XRef::parse_with_limit(bytes, password, reader.max_decoded_bytes, reader.max_cmap_entries)?;
let page_infos = collect_pages(bytes, &xref)?;
crate::par::map_ordered(
page_infos,
|| &xref,
|xref, _, p| build_page_part(p, bytes, xref),
)
.into_iter()
.collect()
}
fn build_page_part(p: pages::PageInfo, bytes: &[u8], xref: &XRef) -> Result<PagePart> {
let contents = decode_contents(bytes, xref, &p.contents)?;
let parts = extract_parts(&contents, &p.resources, p.view, ContentCtx {
data: bytes,
xref: Some(xref),
}, p.rotate);
let (mut width, mut height) = (view_width(p.view), view_height(p.view));
if p.rotate == 90 || p.rotate == 270 {
std::mem::swap(&mut width, &mut height);
}
if parts.norm_rotate == 90 || parts.norm_rotate == 270 {
std::mem::swap(&mut width, &mut height);
}
Ok(PagePart {
width,
height,
glyphs: parts.glyphs,
edges: parts.edges,
norm_rotate: parts.norm_rotate,
graphics: parts.graphics,
})
}
fn build_text_page(p: pages::PageInfo, bytes: &[u8], xref: &XRef) -> Result<TextPage> {
let contents = decode_contents(bytes, xref, &p.contents)?;
let text = extract_text(
&contents,
&p.resources,
p.view,
ContentCtx {
data: bytes,
xref: Some(xref),
},
p.rotate,
);
Ok(TextPage {
width: text.width,
height: text.height,
fonts: text.fonts,
chars: text.chars,
})
}
pub(crate) fn read_pages_and_text(
bytes: &[u8],
password: Option<&str>,
reader: &ReaderSettings,
) -> Result<(Vec<PagePart>, Vec<TextPage>, Vec<String>)> {
let xref = XRef::parse_with_limit(bytes, password, reader.max_decoded_bytes, reader.max_cmap_entries)?;
let page_infos = collect_pages(bytes, &xref)?;
let results: Vec<(PagePart, TextPage)> = crate::par::map_ordered(
page_infos,
|| &xref,
|xref, _, p| build_page_part_and_text(p, bytes, xref),
)
.into_iter()
.collect::<Result<Vec<_>>>()?;
let warnings = xref.take_warnings();
let (parts, texts) = results.into_iter().unzip();
Ok((parts, texts, warnings))
}
fn build_page_part_and_text(
p: pages::PageInfo,
bytes: &[u8],
xref: &XRef,
) -> Result<(PagePart, TextPage)> {
let contents = decode_contents(bytes, xref, &p.contents)?;
let (parts, text) = extract_parts_and_text(
&contents,
&p.resources,
p.view,
ContentCtx {
data: bytes,
xref: Some(xref),
},
p.rotate,
);
let (mut width, mut height) = (view_width(p.view), view_height(p.view));
if p.rotate == 90 || p.rotate == 270 {
std::mem::swap(&mut width, &mut height);
}
if parts.norm_rotate == 90 || parts.norm_rotate == 270 {
std::mem::swap(&mut width, &mut height);
}
let part = PagePart {
width,
height,
glyphs: parts.glyphs,
edges: parts.edges,
norm_rotate: parts.norm_rotate,
graphics: parts.graphics,
};
let tp = TextPage {
width: text.width,
height: text.height,
fonts: text.fonts,
chars: text.chars,
};
Ok((part, tp))
}
#[derive(Debug, Clone, Serialize)]
pub struct ReaderDump {
pub pages: Vec<ReaderDumpPage>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ReaderDumpPage {
pub width: f64,
pub height: f64,
pub norm_rotate: i32,
pub text: TextView,
pub parts: PartsView,
}
#[derive(Debug, Clone, Serialize)]
pub struct TextView {
pub fonts: Vec<TextFont>,
pub chars: Vec<TextChar>,
}
#[derive(Debug, Clone, Serialize)]
pub struct PartsView {
pub glyphs: Vec<GlyphPart>,
pub edges: Vec<EdgePart>,
pub graphics: Vec<GraphicPart>,
}
pub fn read_pages_full(bytes: &[u8], password: Option<&str>) -> Result<ReaderDump> {
read_pages_full_with(bytes, password, &ReaderSettings::default())
}
pub fn read_pages_full_with(
bytes: &[u8],
password: Option<&str>,
reader: &ReaderSettings,
) -> Result<ReaderDump> {
let (parts, texts, warnings) = read_pages_and_text(bytes, password, reader)?;
let pages = parts
.into_iter()
.zip(texts)
.map(|(part, text)| ReaderDumpPage {
width: part.width,
height: part.height,
norm_rotate: part.norm_rotate,
text: TextView {
fonts: text.fonts,
chars: text.chars,
},
parts: PartsView {
glyphs: part.glyphs,
edges: part.edges,
graphics: part.graphics,
},
})
.collect();
Ok(ReaderDump { pages, warnings })
}