pub mod arc;
pub mod error;
pub mod objects;
pub mod operators;
pub mod page;
pub mod resources;
pub mod writer;
pub use error::PdfError;
pub use writer::write_pdf;
use oxideav_core::{
CodecCapabilities, CodecId, CodecInfo, CodecParameters, CodecRegistry, ContainerRegistry,
Encoder, Error, Frame, MediaType, Muxer, Packet, Result, StreamInfo, TimeBase, WriteSeek,
};
pub const CODEC_ID_STR: &str = "pdf";
struct PdfEncoder {
output_params: CodecParameters,
pending: Option<Vec<u8>>,
eof: bool,
}
fn make_encoder(params: &CodecParameters) -> Result<Box<dyn Encoder>> {
let mut output_params = params.clone();
output_params.media_type = MediaType::Video;
output_params.codec_id = CodecId::new(CODEC_ID_STR);
Ok(Box::new(PdfEncoder {
output_params,
pending: None,
eof: false,
}))
}
impl Encoder for PdfEncoder {
fn codec_id(&self) -> &CodecId {
&self.output_params.codec_id
}
fn output_params(&self) -> &CodecParameters {
&self.output_params
}
fn send_frame(&mut self, frame: &Frame) -> Result<()> {
match frame {
Frame::Vector(v) => {
let bytes = write_pdf(v).map_err(Error::from)?;
self.pending = Some(bytes);
Ok(())
}
_ => Err(Error::invalid(
"PDF encoder: only vector frames are accepted (Frame::Vector)",
)),
}
}
fn receive_packet(&mut self) -> Result<Packet> {
if let Some(bytes) = self.pending.take() {
let mut pkt = Packet::new(0, TimeBase::new(1, 1), bytes);
pkt.flags.keyframe = true;
return Ok(pkt);
}
if self.eof {
return Err(Error::Eof);
}
Err(Error::NeedMore)
}
fn flush(&mut self) -> Result<()> {
self.eof = true;
Ok(())
}
}
struct PdfMuxer {
output: Box<dyn WriteSeek>,
written_packet: bool,
}
fn open_muxer(output: Box<dyn WriteSeek>, _streams: &[StreamInfo]) -> Result<Box<dyn Muxer>> {
Ok(Box::new(PdfMuxer {
output,
written_packet: false,
}))
}
impl Muxer for PdfMuxer {
fn format_name(&self) -> &str {
"pdf"
}
fn write_header(&mut self) -> Result<()> {
Ok(())
}
fn write_packet(&mut self, packet: &Packet) -> Result<()> {
if self.written_packet {
return Err(Error::invalid(
"PDF muxer: round-1 supports a single page; got more than one packet",
));
}
use std::io::Write;
self.output.write_all(&packet.data)?;
self.written_packet = true;
Ok(())
}
fn write_trailer(&mut self) -> Result<()> {
Ok(())
}
}
pub fn register_codecs(reg: &mut CodecRegistry) {
let caps = CodecCapabilities::video("pdf_sw")
.with_intra_only(true)
.with_lossless(true);
reg.register(
CodecInfo::new(CodecId::new(CODEC_ID_STR))
.capabilities(caps)
.encoder(make_encoder),
);
}
pub fn register_containers(reg: &mut ContainerRegistry) {
reg.register_muxer("pdf", open_muxer);
reg.register_extension("pdf", "pdf");
}
pub fn register(codecs: &mut CodecRegistry, containers: &mut ContainerRegistry) {
register_codecs(codecs);
register_containers(containers);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registration_adds_encoder_and_muxer() {
let mut codecs = CodecRegistry::new();
let mut containers = ContainerRegistry::new();
register(&mut codecs, &mut containers);
assert!(codecs.has_encoder(&CodecId::new(CODEC_ID_STR)));
assert!(containers.muxer_names().any(|n| n == "pdf"));
}
}