Skip to main content

EditDoc

Struct EditDoc 

Source
pub struct EditDoc<'a> { /* private fields */ }
Expand description

A document plus the edits made to it.

Cheap to create and to drop: it borrows the base and owns only what changed. Cloning copies the overlay’s map and shares its objects, so a save that must not disturb the session works on a clone.

Implementations§

Source§

impl EditDoc<'_>

Source

pub fn draw_page( &mut self, index: impl Into<PageIndex>, limits: &Limits, body: impl FnOnce(&mut Canvas<'_, '_>), ) -> Result<(), Error>

Draw on page index, appending what body draws as one new content stream.

The canvas’s coordinate space is the page as displayed — see Canvas. The stream is wrapped in q/Q and appended to the page’s /Contents array, so the page’s own graphics state cannot leak into the drawing and the drawing’s cannot leak into the page. The page’s existing streams are not rewritten, which is why drawing on a page costs none of the regeneration losses PageEdit documents.

§The resource-merging rule

Fonts, images and graphics states the drawing used are merged into the page’s /Resources under names of this crate’s own PdfrumC<n> series, each checked against the names the page already holds, so a merged name can collide with neither the producer’s nor pdfrum-edit’s FX*. A /Resources the page shares with another page is copied before it is written to, so drawing on one page cannot change another.

use pdfrum::{Color, Document, Point, SaveOptions, StandardFont};

let doc = Document::open("tests/fixtures/hello_world.pdf")?;
let mut edit = doc.edit();
let font = edit.standard_font(StandardFont::Helvetica)?;
edit.draw_page(0, |c| {
    c.text("drawn", &font, 12.0, Point::new(40.0, 40.0), Color::BLACK);
})?;

let mut bytes = Vec::new();
edit.write_to(&mut bytes, &SaveOptions::default())?;
let saved = Document::from_bytes(bytes)?;
assert!(saved.page(0)?.text().to_string().contains("drawn"));
§Errors

Whatever body refused to draw — a character the font has no glyph for, most often — and Error::InlinePage for a page with no object of its own. Nothing is written when the drawing failed.

Source

pub fn draw_page_with_fonts( &mut self, index: impl Into<PageIndex>, limits: &Limits, fonts: &SvgFonts, body: impl FnOnce(&mut Canvas<'_, '_>), ) -> Result<(), Error>

EditDoc::draw_page with the faces an ingested SVG’s <text> is set in; without them a <text> draws nothing and is reported instead.

§Errors

As EditDoc::draw_page.

Source

pub fn draw_pages( &mut self, limits: &Limits, body: impl FnMut(&mut Canvas<'_, '_>), ) -> Result<(), Error>

Draw on every page, one canvas each.

The closure runs once per page in order and is handed that page’s own canvas, so Canvas::size and Canvas::page are the page’s. A page written inline in its parent’s /Kids is skipped rather than refused: a whole-document watermark should not fail because one page of a thousand cannot carry it.

use pdfrum::{Color, Document, SaveOptions, Stroke, Point};

let doc = Document::open("tests/fixtures/hello_world_2_pages.pdf")?;
let mut edit = doc.edit();
edit.draw_pages(|c| {
    let y = c.size().height - 40.0;
    c.line(Point::new(40.0, y), Point::new(c.size().width - 40.0, y),
           Stroke::new(Color::BLACK, 0.5));
})?;
let mut bytes = Vec::new();
edit.write_to(&mut bytes, &SaveOptions::default())?;
assert!(bytes.starts_with(b"%PDF-"));
§Errors

As EditDoc::draw_page, for the first page whose drawing failed.

Source

pub fn draw_pages_with_fonts( &mut self, limits: &Limits, fonts: &SvgFonts, body: impl FnMut(&mut Canvas<'_, '_>), ) -> Result<(), Error>

EditDoc::draw_pages with the faces an ingested SVG’s <text> is set in.

§Errors

As EditDoc::draw_page, for the first page whose drawing failed.

Source§

impl<'a> EditDoc<'a>

Source

pub fn new(base: &'a Document) -> Self

An unedited view of base.

Source

pub fn base(&self) -> &'a Document

The document underneath the edits.

Source

pub fn add(&mut self, obj: Object) -> ObjRef

Add obj as a new indirect object, returning the reference that names it. Generation is always 0: the writer emits nothing else.

Source

pub fn replace(&mut self, r: ObjRef, obj: Object)

Replace what r names.

The base is untouched: the overlay simply answers first from now on.

Source

pub fn remove(&mut self, r: ObjRef)

Remove what r names. It then resolves as null and is not written.

Source

pub fn is_removed(&self, num: u32) -> bool

Whether num was removed.

Source

pub fn edited(&self) -> impl Iterator<Item = (u32, &Arc<Object>)>

The overlay’s objects in ascending number order — everything this editing session added or replaced.

Source

pub fn is_edited(&self, num: u32) -> bool

Whether num has an overlay entry.

Source

pub fn last_object_number(&self) -> u32

The highest object number in play, across the base and the overlay.

Source

pub fn page_state( &self, index: PageIndex, ) -> Result<Option<(ObjRef, Dict, Dict)>, Error>

A page’s dictionary as the session’s edits leave it, with the reference the writer replaces and the resources the page reaches.

Read through the overlay, not the base, so an earlier edit of the same page — a rotation, a stamp — is what a later content rewrite builds on. None for a page written inline in its parent’s /Kids, which has no object to replace.

§Errors

When index is outside the document.

Source

pub fn apply_page( &mut self, page: &PageEdit, shared: &ShareCounts, ) -> Result<(), Error>

Turn one page edit into replacement objects on the session.

shared is shared_objects over the session, computed once by the caller for however many pages it applies.

§Errors

When the page’s index is outside the document.

Source§

impl EditDoc<'_>

Source

pub fn embed_font( &mut self, program: &[u8], encoding: FontEncoding, ) -> Result<EmbeddedFont, Error>

Embed program as a new /Font in this document.

The program kind is detected from its bytes: an OTTO tag is OpenType/CFF, 0x00010000 / true / typ1 is TrueType, and a PFB marker or %!PS banner is Type 1.

§Errors

Error::UnrecognisedFontProgram when no backend can read the bytes, Error::EmptyFontProgram when the face declares no glyphs.

Source

pub fn embed_cid_font( &mut self, program: &[u8], to_unicode: &str, cid_to_gid: &[u8], ) -> Result<EmbeddedFont, Error>

Embed program as a /Type0 + /CIDFontType2 font whose /ToUnicode and /CIDToGIDMap are the caller’s, not generated from the program’s own cmap.

The dictionary chain is the one Self::embed_font builds for FontEncoding::Composite, with three differences, all of them the point of the call:

  • /CIDToGIDMap is a stream holding cid_to_gid verbatim: one big-endian u16 glyph index per CID, indexed by CID (ISO 32000-1 §9.7.4.2). Self::embed_font writes no /CIDToGIDMap at all, which means /Identity — CID is GID.
  • /W is computed per CID from that map rather than per GID from the cmap: each two-byte entry names the glyph whose advance that CID gets, so the array is dense from CID 0 and holds one width per map entry.
  • /ToUnicode is a stream holding to_unicode verbatim.

The descendant is always a /CIDFontType2, because that is what /CIDToGIDMap means: a /CIDFontType0 reaches glyphs with the CID as the glyph index and never consults the map.

EmbeddedFont::encode on the result writes CIDs found by inverting to_unicode, not GIDs found in the program.

§Errors

Error::UnrecognisedFontProgram when no backend can read the bytes, Error::EmptyFontProgram when the face declares no glyphs, Error::EmptyToUnicodeCMap for an empty to_unicode, and Error::BadCidToGidMap when cid_to_gid is empty or is not a whole number of two-byte entries.

Source

pub fn standard_font( &mut self, which: StandardFont, ) -> Result<EmbeddedFont, Error>

Add a non-embedded standard-14 Type 1 font (/BaseFont, /Encoding /WinAnsiEncoding, no /Widths).

§Errors

This path does not fail today; the Result is for symmetry with Self::embed_font and for a caller that wants to handle both the same way.

Source§

impl EditDoc<'_>

Source

pub fn embed_jpeg(&mut self, bytes: &[u8]) -> Result<EmbeddedImage, Error>

Embed a JPEG or JPEG 2000 codestream as a new image XObject.

The bytes become the stream verbatim under /DCTDecode or /JPXDecode; nothing is decoded or re-encoded. /Width, /Height, /ColorSpace and /BitsPerComponent are read from the codestream’s own header, which is the file’s statement of them and overrides any a caller could pass.

§Errors

Error::UnrecognisedImageData when the bytes are neither a JPEG nor a JPEG 2000 codestream, or their header cannot be read.

Source

pub fn embed_image( &mut self, pixels: &[u8], width: u32, height: u32, format: PixelFormat, ) -> Result<EmbeddedImage, Error>

Embed raw interleaved samples as a new image XObject.

The samples are stored uncompressed and the stream writer flate-encodes them (there is no /Filter on the dictionary this writes). An PixelFormat::Rgba8 alpha channel is split off into a separate /DeviceGray /SMask image; the colour channels keep their own stream.

§Errors

Error::EmptyImage when either dimension is zero, and Error::ImageDataLength when pixels is not exactly the length the dimensions and format require.

Source§

impl EditDoc<'_>

Source

pub fn stamp_text( &mut self, text: &str, options: &StampOptions, limits: &Limits, ) -> Result<(), Error>

Draw text over every page.

The text is set in StampOptions::font at StampOptions::font_size, its box placed by StampOptions::position on the page as displayed, then turned by StampOptions::angle about the box’s centre. One /Font object serves every page. Each page’s existing content is left as it was; the stamp is one more stream after it, so it paints on top.

use pdfrum::{Document, SaveOptions, StampOptions, StampPosition};

let doc = Document::open("tests/fixtures/hello_world_2_pages.pdf")?;
let mut edit = doc.edit();
edit.stamp_text(
    "DRAFT",
    &StampOptions::builder()
        .position(StampPosition::BottomRight)
        .opacity(0.5)
        .build(),
)?;
let mut bytes = Vec::new();
edit.write_to(&mut bytes, &SaveOptions::default())?;

let stamped = pdfrum::Document::from_bytes(bytes)?;
assert!(stamped.page(1)?.text().to_string().contains("DRAFT"));
§Errors

When the font cannot be added, or a page cannot be opened.

Source

pub fn stamp_image( &mut self, image: &EmbeddedImage, width: f64, options: &StampOptions, limits: &Limits, ) -> Result<(), Error>

Draw image over every page, width points wide with its aspect ratio kept.

Placed and turned as EditDoc::stamp_text places text, at StampOptions::opacity; the font fields are not read. The image is one this session embedded through EditDoc::embed_jpeg or EditDoc::embed_image, and one XObject serves every page.

use pdfrum::{Document, PixelFormat, SaveOptions, StampOptions};

let doc = Document::open("tests/fixtures/hello_world.pdf")?;
let mut edit = doc.edit();
// A two-by-one image: red, then blue.
let image = edit.embed_image(&[255, 0, 0, 0, 0, 255], 2, 1, PixelFormat::Rgb8)?;
edit.stamp_image(&image, 200.0, &StampOptions::builder().opacity(0.5).build())?;
let mut bytes = Vec::new();
edit.write_to(&mut bytes, &SaveOptions::default())?;
assert!(bytes.starts_with(b"%PDF-"));
§Errors

Error::EmptyImage when width is not positive or the image has no width, and Error::PageIndexOutOfRange when a page cannot be opened.

Source§

impl EditDoc<'_>

Source

pub fn compile_svg( &mut self, svg: &str, limits: &Limits, fonts: &SvgFonts, ) -> Result<(SvgForm, SvgIngestReport), Error>

Available on crate feature svg-import only.

Compile an SVG document once, into a Form XObject any number of pages can place.

The deduplicating half of Canvas::draw_svg. That method writes the SVG’s operators inline into the page it is drawing on, which is right for one placement and wasteful for many: the same logo on twenty pages becomes twenty copies of the same content. This compiles the document into a single /Subtype /Form object with its own /BBox and /Resources, and Canvas::place_svg then writes one Do per page against it.

Nothing about the mapping differs — a form’s content stream holds the same operators draw_svg would have written, and the returned SvgIngestReport is the same report. What differs is that the operators are written once, and that the fit is chosen per placement rather than baked in: the form’s box is the SVG’s own, so one compiled logo can be placed SvgFit::Contain on one page and SvgFit::Cover on another.

§Errors

Error::Svg when usvg cannot resolve the document, exactly as Canvas::draw_svg reports it.

use pdfrum::{Document, Rect, SvgFit};

const LOGO: &str = "<svg xmlns=\"http://www.w3.org/2000/svg\" \
    viewBox=\"0 0 10 10\">\
    <rect width=\"10\" height=\"10\" fill=\"#0a0\"/></svg>";

let doc = Document::open("tests/fixtures/hello_world.pdf")?;
let mut edit = doc.edit();
let (logo, _) = edit.compile_svg(LOGO)?;
assert_eq!(logo.bbox().width(), 10.0);
Source

pub fn compile_svg_from( &mut self, svg: &str, resources_dir: Option<&Path>, limits: &Limits, ) -> Result<(SvgForm, SvgIngestReport), Error>

Available on crate feature svg-import only.

Compile an SVG whose relative <image href> links resolve against resources_dir.

DocEdit::compile_svg is this with no directory, which is right for a document held in memory; one read from a file wants the file’s own directory here, or its linked images do not load. The same pairing Canvas::draw_svg and Canvas::draw_svg_from have.

§Errors

As DocEdit::compile_svg.

Source

pub fn compile_svg_from_with_fonts( &mut self, svg: &str, resources_dir: Option<&Path>, limits: &Limits, fonts: &SvgFonts, ) -> Result<(SvgForm, SvgIngestReport), Error>

Available on crate feature svg-import only.

[EditDoc::compile_svg_from] with the faces the SVG’s <text> is set in; without them a <text> draws nothing and is reported instead.

§Errors

As [EditDoc::compile_svg_from].

Trait Implementations§

Source§

impl<'a> Clone for EditDoc<'a>

Source§

fn clone(&self) -> EditDoc<'a>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for EditDoc<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Resolve for EditDoc<'_>

Source§

fn fetch(&self, r: ObjRef) -> Result<Arc<Object>, Error>

The object stored under r, or why it could not be produced. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for EditDoc<'a>

§

impl<'a> RefUnwindSafe for EditDoc<'a>

§

impl<'a> Send for EditDoc<'a>

§

impl<'a> Sync for EditDoc<'a>

§

impl<'a> Unpin for EditDoc<'a>

§

impl<'a> UnsafeUnpin for EditDoc<'a>

§

impl<'a> UnwindSafe for EditDoc<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.