mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-11 08:51:27 +00:00
One image has its own limit, about 6 s to 17 s, but nothing bounded a file of thousands of images made to reach it: an operation took that long for each, and an image sharing /JBIG2Globals decodes them again. Image::decode_within (hayro-jbig2 patch 11) spends from what a run may still do, starting with its page's pixels, and charge_reading charges reading a stream and its globals, 4 steps a byte, before anything is parsed, so an image that fails has paid too. pdfcraft_filters::decode_jbig2_within takes it, and ImageBudget in cos is that allowance: 2^34 steps, about 1,000 scanned pages, and 2^13 more for each byte of the file, so a long scan is read whole. An image past it spends all that's left, and later ones aren't read, globals included. Optimize leaves such an image as it is and counts it in Report::images_refused. Image export leaves it out, with the reason, and stops at 2 GiB of files held. Office export shares one allowance, leaves the picture out and says the page was truncated. Redaction removes it whole, as any image it can't read. Rendering gives a page's images 2^30 steps between them, inside the watchdog, and each page starts afresh in Context::new as its other budgets do (hayro-syntax patch 31, hayro-interpret patch 73). It's a thread-local, unbounded until a page sets it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
58 lines
2.5 KiB
Rust
58 lines
2.5 KiB
Rust
//! pdfcraft-cos — the PDF object layer (L1, standalone with `pdfcraft-filters`).
|
||
//!
|
||
//! Parses the COS object graph lazily and tolerantly, keeps edits in a copy-on-write overlay
|
||
//! (cheap snapshots for undo), and writes documents back either incrementally (original bytes
|
||
//! untouched) or as a full, garbage-collected rewrite. The PDF object graph *is* PdfCraft's
|
||
//! document model (plan/architecture.md §5, ADR-0010).
|
||
//!
|
||
//! Done: every cross-reference form (tables, streams, hybrid `/XRefStm`), object streams, repair
|
||
//! by scanning, encryption on load and save (R2–R6, crypt filters, `/EFF`), incremental writing,
|
||
//! and full saves that pack objects into compressed object streams with a cross-reference stream.
|
||
//! Not yet: streaming very large files within a memory budget (`ByteSource`,
|
||
//! `core.lazy-loading`), and linearized output (`core.linearization`, M11). See the README.
|
||
|
||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||
|
||
mod budget;
|
||
mod bytes;
|
||
mod document;
|
||
mod object;
|
||
pub mod page_labels;
|
||
mod parser;
|
||
mod security;
|
||
mod writer;
|
||
|
||
pub use budget::{DecodeBudget, ImageBudget, MAX_PAGE_DECODED, MAX_PAGE_FORMS};
|
||
pub use bytes::Bytes;
|
||
pub use document::{Document, ReadState, Revision, SourceIdentity, XrefEntry};
|
||
pub use object::{Dict, MAX_DECODED, MAX_JBIG2_GLOBALS, Name, ObjRef, Object, PdfString, Stream};
|
||
pub use parser::{Lexer, is_whitespace, parse_indirect};
|
||
pub use pdfcraft_crypt::{
|
||
Algorithm, Auth, Method as CryptMethod, NewEncryption, Permissions, SecurityHandler, effective_password, pdfdoc_byte, pdfdoc_char,
|
||
};
|
||
pub use writer::{SaveOptions, pdf_date, serialize, write_full, write_incremental};
|
||
|
||
#[derive(Debug, thiserror::Error, Clone, PartialEq)]
|
||
pub enum CosError {
|
||
#[error("not a PDF file (no %PDF- header)")]
|
||
NotPdf,
|
||
#[error("syntax error at byte {offset}: {detail}")]
|
||
Syntax { offset: usize, detail: String },
|
||
#[error("object {0} is missing")]
|
||
MissingObject(u32),
|
||
#[error("object {0} is not a dictionary")]
|
||
NotADictionary(ObjRef),
|
||
#[error("stream data could not be decoded: {0}")]
|
||
Filter(String),
|
||
#[error("the document is protected by a password")]
|
||
NeedsPassword,
|
||
#[error("the password is incorrect")]
|
||
WrongPassword,
|
||
#[error("unsupported security: {0}")]
|
||
Security(String),
|
||
#[error("internal lock poisoned")]
|
||
Poisoned,
|
||
#[error("this document was opened read-only with a decode limit and cannot be saved")]
|
||
ReadOnlyLimit,
|
||
}
|