mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-28 03:58:45 -06:00
The PDF/A device-colour scan split content streams only on spaces and line feeds, so `0 0 0 1 k` written with carriage returns, tabs, form feeds or NULs between the tokens went unseen: verify reported no DeviceCMYK, and convert gave the CMYK page an sRGB output intent. Split on all six whitespace characters of ISO 32000-2 §7.2.3, using the lexer's own `is_whitespace` (now exported from pdfcraft-cos) instead of a second list. Adds tests::device_colour_is_found_after_any_whitespace, which fails before this change; its gray and RGB controls check that those pages still get the sRGB output intent.
56 lines
2.3 KiB
Rust
56 lines
2.3 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 bytes;
|
||
mod document;
|
||
mod object;
|
||
pub mod page_labels;
|
||
mod parser;
|
||
mod security;
|
||
mod writer;
|
||
|
||
pub use bytes::Bytes;
|
||
pub use document::{Document, Revision, SourceIdentity, XrefEntry};
|
||
pub use object::{Dict, MAX_DECODED, 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,
|
||
}
|