Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chunks and renumbering #18

Merged
merged 4 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ keywords = ["pdf", "writer"]
[dependencies]
bitflags = "1.1"
itoa = "1"
memchr = "2"
ryu = "1"

[dev-dependencies]
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ A step-by-step PDF writer.
pdf-writer = "0.8"
```

The entry point into the API is the main `PdfWriter`, which constructs the
document into one big internal buffer. The top-level writer has many methods to
create specialized writers for specific PDF objects. These all follow the same
general pattern: They borrow the main buffer mutably, expose a builder pattern
for writing individual fields in a strongly typed fashion and finish up the
object when dropped.
The entry point into the API is the main `Pdf`, which constructs the document
into one big internal buffer. The top-level writer has many methods to create
specialized writers for specific PDF objects. These all follow the same general
pattern: They borrow the main buffer mutably, expose a builder pattern for
writing individual fields in a strongly typed fashion and finish up the object
when dropped.

There are a few more top-level structs with internal buffers, like the builder
for `Content` streams, but wherever possible buffers are borrowed from parent
Expand All @@ -24,24 +24,24 @@ writers to minimize allocations.
The following example creates a PDF with a single, empty A4 page.

```rust
use pdf_writer::{PdfWriter, Rect, Ref};
use pdf_writer::{Pdf, Rect, Ref};

// Define some indirect reference ids we'll use.
let catalog_id = Ref::new(1);
let page_tree_id = Ref::new(2);
let page_id = Ref::new(3);

// Write a document catalog and a page tree with one A4 page that uses no resources.
let mut writer = PdfWriter::new();
writer.catalog(catalog_id).pages(page_tree_id);
writer.pages(page_tree_id).kids([page_id]).count(1);
writer.page(page_id)
let mut pdf = Pdf::new();
pdf.catalog(catalog_id).pages(page_tree_id);
pdf.pages(page_tree_id).kids([page_id]).count(1);
pdf.page(page_id)
.parent(page_tree_id)
.media_box(Rect::new(0.0, 0.0, 595.0, 842.0))
.resources();

// Finish with cross-reference table and trailer and write to file.
std::fs::write("target/empty.pdf", writer.finish())?;
std::fs::write("target/empty.pdf", pdf.finish())?;
```

For more examples, check out the [examples folder] in the repository.
Expand Down
8 changes: 4 additions & 4 deletions benches/oneshot.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::io::Write;

use pdf_writer::{Content, PdfWriter, Rect, Ref};
use pdf_writer::{Content, Pdf, Rect, Ref};

fn bench_alloc() -> Vec<u8> {
Vec::with_capacity(16)
Expand Down Expand Up @@ -50,12 +50,12 @@ fn bench_content() -> Vec<u8> {
c.finish()
}

fn bench_new() -> PdfWriter {
PdfWriter::new()
fn bench_new() -> Pdf {
Pdf::new()
}

fn bench_full() -> Vec<u8> {
let mut w = PdfWriter::new();
let mut w = Pdf::new();
w.catalog(Ref::new(1));
w.page(Ref::new(2)).media_box(Rect::new(0.0, 0.0, 595.0, 842.0));
w.stream(Ref::new(3), &b"ABCDEFG"[..]);
Expand Down
16 changes: 8 additions & 8 deletions examples/hello.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! This example gives you a first introduction on how to use pdf-writer.

use pdf_writer::types::{ActionType, AnnotationType, BorderType};
use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr};
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref, Str, TextStr};

fn main() -> std::io::Result<()> {
// Start writing.
let mut writer = PdfWriter::new();
let mut pdf = Pdf::new();

// Define some indirect reference ids we'll use.
let catalog_id = Ref::new(1);
Expand All @@ -16,13 +16,13 @@ fn main() -> std::io::Result<()> {
let font_name = Name(b"F1");

// Write the document catalog with a reference to the page tree.
writer.catalog(catalog_id).pages(page_tree_id);
pdf.catalog(catalog_id).pages(page_tree_id);

// Write the page tree with a single child page.
writer.pages(page_tree_id).kids([page_id]).count(1);
pdf.pages(page_tree_id).kids([page_id]).count(1);

// Write a page.
let mut page = writer.page(page_id);
let mut page = pdf.page(page_id);

// Set the size to A4 (measured in points) using `media_box` and set the
// text object we'll write later as the page's contents.
Expand Down Expand Up @@ -68,7 +68,7 @@ fn main() -> std::io::Result<()> {
// Specify the font we want to use. Because Helvetica is one of the 14 base
// fonts shipped with every PDF reader, we don't have to embed any font
// data.
writer.type1_font(font_id).base_font(Name(b"Helvetica"));
pdf.type1_font(font_id).base_font(Name(b"Helvetica"));

// Write a line of text, with the font specified in the resource list
// before, at a font size of 14.0, starting at coordinates (108.0, 734.0)
Expand All @@ -83,11 +83,11 @@ fn main() -> std::io::Result<()> {
content.next_line(108.0, 734.0);
content.show(Str(b"Hello World from Rust!"));
content.end_text();
writer.stream(content_id, &content.finish());
pdf.stream(content_id, &content.finish());

// Finish writing (this automatically creates the cross-reference table and
// file trailer) and retrieve the resulting byte buffer.
let buf: Vec<u8> = writer.finish();
let buf: Vec<u8> = pdf.finish();

// Write the thing to a file.
std::fs::write("target/hello.pdf", buf)
Expand Down
16 changes: 8 additions & 8 deletions examples/icc.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! This example shows how to use ICC-based color spaces.

use pdf_writer::writers::ColorSpace;
use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref};
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref};

fn main() -> std::io::Result<()> {
// Start writing.
let mut writer = PdfWriter::new();
let mut pdf = Pdf::new();

// Define some indirect reference ids we'll use.
let catalog_id = Ref::new(1);
Expand All @@ -23,11 +23,11 @@ fn main() -> std::io::Result<()> {
let color_space_name = Name(b"sRGB");

// Set up the page tree. For more details see `hello.rs`.
writer.catalog(catalog_id).pages(page_tree_id);
writer.pages(page_tree_id).kids([page_id]).count(1);
pdf.catalog(catalog_id).pages(page_tree_id);
pdf.pages(page_tree_id).kids([page_id]).count(1);

// Write a page.
let mut page = writer.page(page_id);
let mut page = pdf.page(page_id);

// Create an A4 page.
page.media_box(Rect::new(0.0, 0.0, 595.0, 842.0));
Expand Down Expand Up @@ -79,14 +79,14 @@ fn main() -> std::io::Result<()> {
content.close_and_stroke();

// Write the content stream.
writer.stream(content_id, &content.finish());
pdf.stream(content_id, &content.finish());

// Read the ICC profile from a file.
let icc_data = std::fs::read("examples/sRGB_v4.icc")?;
// Start writing the ICC profile stream. In production use, you would
// compress the data stream with the `FlateDecode` filter. Check the
// `image.rs` example for details.
let mut icc_profile = writer.icc_profile(icc_id, &icc_data);
let mut icc_profile = pdf.icc_profile(icc_id, &icc_data);

// PDF requires metadata about the ICC profile. We provide it as entries in
// the stream dictionary. The `n` entry is required and specifies the number
Expand All @@ -104,5 +104,5 @@ fn main() -> std::io::Result<()> {
icc_profile.finish();

// Write the thing to a file.
std::fs::write("target/icc.pdf", writer.finish())
std::fs::write("target/icc.pdf", pdf.finish())
}
18 changes: 9 additions & 9 deletions examples/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

use image::{ColorType, GenericImageView, ImageFormat};
use miniz_oxide::deflate::{compress_to_vec_zlib, CompressionLevel};
use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref};
use pdf_writer::{Content, Filter, Finish, Name, Pdf, Rect, Ref};

fn main() -> std::io::Result<()> {
// Start writing.
let mut writer = PdfWriter::new();
let mut pdf = Pdf::new();

// Define some indirect reference ids we'll use.
let catalog_id = Ref::new(1);
Expand All @@ -18,12 +18,12 @@ fn main() -> std::io::Result<()> {
let image_name = Name(b"Im1");

// Set up the page tree. For more details see `hello.rs`.
writer.catalog(catalog_id).pages(page_tree_id);
writer.pages(page_tree_id).kids([page_id]).count(1);
pdf.catalog(catalog_id).pages(page_tree_id);
pdf.pages(page_tree_id).kids([page_id]).count(1);

// Specify one A4 page and map the image name "Im1" to the id of the
// embedded image stream.
let mut page = writer.page(page_id);
let mut page = pdf.page(page_id);
let a4 = Rect::new(0.0, 0.0, 595.0, 842.0);
page.media_box(a4);
page.parent(page_tree_id);
Expand Down Expand Up @@ -74,7 +74,7 @@ fn main() -> std::io::Result<()> {
};

// Write the stream for the image we want to embed.
let mut image = writer.image_xobject(image_id, &encoded);
let mut image = pdf.image_xobject(image_id, &encoded);
image.filter(filter);
image.width(dynamic.width() as i32);
image.height(dynamic.height() as i32);
Expand All @@ -87,7 +87,7 @@ fn main() -> std::io::Result<()> {

// Add SMask if the image has transparency.
if let Some(encoded) = &mask {
let mut s_mask = writer.image_xobject(s_mask_id, encoded);
let mut s_mask = pdf.image_xobject(s_mask_id, encoded);
s_mask.filter(filter);
s_mask.width(dynamic.width() as i32);
s_mask.height(dynamic.height() as i32);
Expand Down Expand Up @@ -117,8 +117,8 @@ fn main() -> std::io::Result<()> {
content.transform([w, 0.0, 0.0, h, x, y]);
content.x_object(image_name);
content.restore_state();
writer.stream(content_id, &content.finish());
pdf.stream(content_id, &content.finish());

// Write the thing to a file.
std::fs::write("target/image.pdf", writer.finish())
std::fs::write("target/image.pdf", pdf.finish())
}
14 changes: 7 additions & 7 deletions examples/separations.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! This example shows how to use Separation color spaces.

use pdf_writer::writers::ColorSpace;
use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref};
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref};

fn main() -> std::io::Result<()> {
// Start writing.
let mut writer = PdfWriter::new();
let mut pdf = Pdf::new();

// Define some indirect reference ids we'll use.
let catalog_id = Ref::new(1);
Expand All @@ -20,11 +20,11 @@ fn main() -> std::io::Result<()> {
let hot_pink_name = Name(b"AcmePink");

// Set up the page tree. For more details see `hello.rs`.
writer.catalog(catalog_id).pages(page_tree_id);
writer.pages(page_tree_id).kids([page_id]).count(1);
pdf.catalog(catalog_id).pages(page_tree_id);
pdf.pages(page_tree_id).kids([page_id]).count(1);

// Write a page.
let mut page = writer.page(page_id);
let mut page = pdf.page(page_id);

// Create an A4 page.
let width = 595.0;
Expand Down Expand Up @@ -172,8 +172,8 @@ fn main() -> std::io::Result<()> {
}

// Write the content stream.
writer.stream(content_id, &content.finish());
pdf.stream(content_id, &content.finish());

// Write the thing to a file.
std::fs::write("target/separations.pdf", writer.finish())
std::fs::write("target/separations.pdf", pdf.finish())
}
32 changes: 32 additions & 0 deletions src/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,35 @@ impl BorderType {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_annotations() {
test!(
crate::tests::slice(|w| {
let mut page = w.page(Ref::new(1));
let mut annots = page.annotations();
annots.push().rect(Rect::new(0.0, 0.0, 1.0, 1.0));
annots.push().rect(Rect::new(1.0, 1.0, 0.0, 0.0));
annots.finish();
page.bleed_box(Rect::new(-100.0, -100.0, 100.0, 100.0));
}),
b"1 0 obj",
b"<<",
b" /Type /Page",
b" /Annots [<<",
b" /Type /Annot",
b" /Rect [0 0 1 1]",
b" >> <<",
b" /Type /Annot",
b" /Rect [1 1 0 0]",
b" >>]",
b" /BleedBox [-100 -100 100 100]",
b">>",
b"endobj\n\n",
);
}
}
12 changes: 12 additions & 0 deletions src/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub trait BufExt {
fn push_decimal(&mut self, value: f32);
fn push_hex(&mut self, value: u8);
fn push_hex_u16(&mut self, value: u16);
fn push_octal(&mut self, value: u8);
}

impl BufExt for Vec<u8> {
Expand Down Expand Up @@ -67,4 +68,15 @@ impl BufExt for Vec<u8> {
self.push_hex((value >> 8) as u8);
self.push_hex(value as u8);
}

#[inline]
fn push_octal(&mut self, value: u8) {
fn octal(b: u8) -> u8 {
b'0' + b
}

self.push(octal(value >> 6));
self.push(octal((value >> 3) & 63));
self.push(octal(value & 7));
}
}
Loading