Skip to content

Commit 441dc20

Browse files
authored
Chunks and renumbering (#18)
1 parent c01bbc4 commit 441dc20

22 files changed

Lines changed: 1254 additions & 754 deletions

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ keywords = ["pdf", "writer"]
1313
[dependencies]
1414
bitflags = "1.1"
1515
itoa = "1"
16+
memchr = "2"
1617
ryu = "1"
1718

1819
[dev-dependencies]

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ A step-by-step PDF writer.
99
pdf-writer = "0.8"
1010
```
1111

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

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

2626
```rust
27-
use pdf_writer::{PdfWriter, Rect, Ref};
27+
use pdf_writer::{Pdf, Rect, Ref};
2828

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

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

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

4747
For more examples, check out the [examples folder] in the repository.

benches/oneshot.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::io::Write;
22

3-
use pdf_writer::{Content, PdfWriter, Rect, Ref};
3+
use pdf_writer::{Content, Pdf, Rect, Ref};
44

55
fn bench_alloc() -> Vec<u8> {
66
Vec::with_capacity(16)
@@ -50,12 +50,12 @@ fn bench_content() -> Vec<u8> {
5050
c.finish()
5151
}
5252

53-
fn bench_new() -> PdfWriter {
54-
PdfWriter::new()
53+
fn bench_new() -> Pdf {
54+
Pdf::new()
5555
}
5656

5757
fn bench_full() -> Vec<u8> {
58-
let mut w = PdfWriter::new();
58+
let mut w = Pdf::new();
5959
w.catalog(Ref::new(1));
6060
w.page(Ref::new(2)).media_box(Rect::new(0.0, 0.0, 595.0, 842.0));
6161
w.stream(Ref::new(3), &b"ABCDEFG"[..]);

examples/hello.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! This example gives you a first introduction on how to use pdf-writer.
22
33
use pdf_writer::types::{ActionType, AnnotationType, BorderType};
4-
use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr};
4+
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref, Str, TextStr};
55

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

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

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

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

2424
// Write a page.
25-
let mut page = writer.page(page_id);
25+
let mut page = pdf.page(page_id);
2626

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

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

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

9292
// Write the thing to a file.
9393
std::fs::write("target/hello.pdf", buf)

examples/icc.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! This example shows how to use ICC-based color spaces.
22
33
use pdf_writer::writers::ColorSpace;
4-
use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref};
4+
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref};
55

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

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

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

2929
// Write a page.
30-
let mut page = writer.page(page_id);
30+
let mut page = pdf.page(page_id);
3131

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

8181
// Write the content stream.
82-
writer.stream(content_id, &content.finish());
82+
pdf.stream(content_id, &content.finish());
8383

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

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

106106
// Write the thing to a file.
107-
std::fs::write("target/icc.pdf", writer.finish())
107+
std::fs::write("target/icc.pdf", pdf.finish())
108108
}

examples/image.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
33
use image::{ColorType, GenericImageView, ImageFormat};
44
use miniz_oxide::deflate::{compress_to_vec_zlib, CompressionLevel};
5-
use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref};
5+
use pdf_writer::{Content, Filter, Finish, Name, Pdf, Rect, Ref};
66

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

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

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

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

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

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

122122
// Write the thing to a file.
123-
std::fs::write("target/image.pdf", writer.finish())
123+
std::fs::write("target/image.pdf", pdf.finish())
124124
}

examples/separations.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! This example shows how to use Separation color spaces.
22
33
use pdf_writer::writers::ColorSpace;
4-
use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref};
4+
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref};
55

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

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

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

2626
// Write a page.
27-
let mut page = writer.page(page_id);
27+
let mut page = pdf.page(page_id);
2828

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

174174
// Write the content stream.
175-
writer.stream(content_id, &content.finish());
175+
pdf.stream(content_id, &content.finish());
176176

177177
// Write the thing to a file.
178-
std::fs::write("target/separations.pdf", writer.finish())
178+
std::fs::write("target/separations.pdf", pdf.finish())
179179
}

src/annotations.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,35 @@ impl BorderType {
480480
}
481481
}
482482
}
483+
484+
#[cfg(test)]
485+
mod tests {
486+
use super::*;
487+
488+
#[test]
489+
fn test_annotations() {
490+
test!(
491+
crate::tests::slice(|w| {
492+
let mut page = w.page(Ref::new(1));
493+
let mut annots = page.annotations();
494+
annots.push().rect(Rect::new(0.0, 0.0, 1.0, 1.0));
495+
annots.push().rect(Rect::new(1.0, 1.0, 0.0, 0.0));
496+
annots.finish();
497+
page.bleed_box(Rect::new(-100.0, -100.0, 100.0, 100.0));
498+
}),
499+
b"1 0 obj",
500+
b"<<",
501+
b" /Type /Page",
502+
b" /Annots [<<",
503+
b" /Type /Annot",
504+
b" /Rect [0 0 1 1]",
505+
b" >> <<",
506+
b" /Type /Annot",
507+
b" /Rect [1 1 0 0]",
508+
b" >>]",
509+
b" /BleedBox [-100 -100 100 100]",
510+
b">>",
511+
b"endobj\n\n",
512+
);
513+
}
514+
}

src/buf.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub trait BufExt {
88
fn push_decimal(&mut self, value: f32);
99
fn push_hex(&mut self, value: u8);
1010
fn push_hex_u16(&mut self, value: u16);
11+
fn push_octal(&mut self, value: u8);
1112
}
1213

1314
impl BufExt for Vec<u8> {
@@ -67,4 +68,15 @@ impl BufExt for Vec<u8> {
6768
self.push_hex((value >> 8) as u8);
6869
self.push_hex(value as u8);
6970
}
71+
72+
#[inline]
73+
fn push_octal(&mut self, value: u8) {
74+
fn octal(b: u8) -> u8 {
75+
b'0' + b
76+
}
77+
78+
self.push(octal(value >> 6));
79+
self.push(octal((value >> 3) & 63));
80+
self.push(octal(value & 7));
81+
}
7082
}

0 commit comments

Comments
 (0)