Skip to content

Commit e430fa5

Browse files
committed
feat: added a font abstraction layer for ttf fonts
1 parent 4465d4c commit e430fa5

36 files changed

Lines changed: 3259 additions & 612 deletions

Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ path = "src/lib.rs"
2323
jpeg-decoder = "0.3.2"
2424
miniz_oxide = "0.8.9"
2525
png = "0.18.0"
26+
ttf-parser = { version = "0.20", features = ["variable-fonts"] }
2627

2728
# Target-specific configuration to ensure correct behavior without consumer-side feature flags
2829
# For non-WASM targets, use lopdf with its default native features

build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ fn main() {
99

1010
// This ensures that the build script is re-run when the target changes
1111
println!("cargo:rerun-if-env-changed=TARGET");
12-
}
12+
}

examples/basic_usage.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
//! This example demonstrates the core functionality of the library
44
//! including PDF creation, OCG layers, and image embedding.
55
6-
use hipdf::ocg::{OCGManager, Layer};
7-
use hipdf::lopdf::{Document, content::{Content, Operation}, dictionary};
6+
use hipdf::lopdf::{
7+
content::{Content, Operation},
8+
dictionary, Document,
9+
};
10+
use hipdf::ocg::{Layer, OCGManager};
811

912
fn main() -> Result<(), Box<dyn std::error::Error>> {
1013
println!("🚀 Creating basic PDF with hipdf...");
@@ -34,12 +37,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3437
// Add some text content
3538
operations.extend(vec![
3639
Operation::new("BT", vec![]),
37-
Operation::new("Tf", vec![
38-
hipdf::lopdf::Object::Name(b"F1".to_vec()),
39-
12.into()
40-
]),
40+
Operation::new(
41+
"Tf",
42+
vec![hipdf::lopdf::Object::Name(b"F1".to_vec()), 12.into()],
43+
),
4144
Operation::new("Td", vec![50.into(), 750.into()]),
42-
Operation::new("Tj", vec![hipdf::lopdf::Object::string_literal("Hello from hipdf!")]),
45+
Operation::new(
46+
"Tj",
47+
vec![hipdf::lopdf::Object::string_literal("Hello from hipdf!")],
48+
),
4349
Operation::new("ET", vec![]),
4450
]);
4551

@@ -81,7 +87,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
8187
"Pages" => hipdf::lopdf::Object::Reference(pages_id),
8288
});
8389

84-
doc.trailer.set("Root", hipdf::lopdf::Object::Reference(catalog_id));
90+
doc.trailer
91+
.set("Root", hipdf::lopdf::Object::Reference(catalog_id));
8592

8693
// Save the document
8794
let output_path = "examples/basic_example.pdf";
@@ -96,4 +103,4 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
96103
println!(" • Document structure");
97104

98105
Ok(())
99-
}
106+
}

examples/example_embed_pdf.rs

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,91 @@
1-
use hipdf::lopdf::{Document, dictionary, Object, Stream, content::Content};
2-
use hipdf::embed_pdf::{PdfEmbedder, EmbedOptions, MultiPageLayout, PageRange, GridFillOrder};
1+
use hipdf::embed_pdf::{EmbedOptions, GridFillOrder, MultiPageLayout, PageRange, PdfEmbedder};
2+
use hipdf::lopdf::{content::Content, dictionary, Document, Object, Stream};
33
use std::collections::HashMap;
44

55
fn embed_pdf_example() -> Result<(), Box<dyn std::error::Error>> {
66
// Create a new PDF document
77
let mut doc = Document::with_version("1.5");
8-
8+
99
// Initialize the PDF embedder
1010
let mut embedder = PdfEmbedder::new();
11-
11+
1212
// Load the source PDF you want to embed
1313
let source_pdf = embedder.load_pdf("source_document.pdf")?;
14-
14+
1515
// Example 1: Simple single page embedding
1616
let single_page_options = EmbedOptions::new()
17-
.at_position(50.0, 700.0) // x, y position
18-
.with_max_size(200.0, 200.0) // max width, height
17+
.at_position(50.0, 700.0) // x, y position
18+
.with_max_size(200.0, 200.0) // max width, height
1919
.with_page_range(PageRange::Single(0)); // embed first page only
20-
20+
2121
// Example 2: Grid layout with multiple pages
2222
let grid_options = EmbedOptions::new()
2323
.at_position(50.0, 400.0)
24-
.with_max_size(100.0, 120.0) // size per page
24+
.with_max_size(100.0, 120.0) // size per page
2525
.with_layout(MultiPageLayout::Grid {
2626
columns: 3,
2727
gap_x: 10.0,
2828
gap_y: 15.0,
2929
fill_order: GridFillOrder::RowFirst,
3030
})
3131
.with_page_range(PageRange::Range(0, 5)); // first 6 pages
32-
32+
3333
// Example 3: Horizontal layout with scaling
3434
let horizontal_options = EmbedOptions::new()
3535
.at_position(50.0, 200.0)
36-
.with_scale(0.2) // 20% of original size
36+
.with_scale(0.2) // 20% of original size
3737
.with_layout(MultiPageLayout::Horizontal { gap: 20.0 })
3838
.with_page_range(PageRange::Pages(vec![0, 2, 4])); // specific pages
39-
39+
4040
// Example 4: Custom rotation and positioning
4141
let rotated_options = EmbedOptions::new()
4242
.at_position(300.0, 500.0)
4343
.with_scale(0.15)
44-
.with_rotation(45.0) // 45 degree rotation
44+
.with_rotation(45.0) // 45 degree rotation
4545
.with_layout(MultiPageLayout::FirstPageOnly);
46-
46+
4747
// Set up document structure
4848
let pages_id = doc.add_object(dictionary! {
4949
"Type" => "Pages",
5050
"Count" => 1,
5151
});
52-
52+
5353
let mut page_ops = Vec::new();
5454
let mut all_xobjects = HashMap::new();
55-
55+
5656
// Embed the PDFs and collect operations
5757
let result = embedder.embed_pdf(&mut doc, &source_pdf, &single_page_options)?;
5858
page_ops.extend(result.operations);
5959
all_xobjects.extend(result.xobject_resources);
60-
60+
6161
let result = embedder.embed_pdf(&mut doc, &source_pdf, &grid_options)?;
6262
page_ops.extend(result.operations);
6363
all_xobjects.extend(result.xobject_resources);
64-
64+
6565
let result = embedder.embed_pdf(&mut doc, &source_pdf, &horizontal_options)?;
6666
page_ops.extend(result.operations);
6767
all_xobjects.extend(result.xobject_resources);
68-
68+
6969
let result = embedder.embed_pdf(&mut doc, &source_pdf, &rotated_options)?;
7070
page_ops.extend(result.operations);
7171
all_xobjects.extend(result.xobject_resources);
72-
72+
7373
// Create page content and resources
74-
let content = Content { operations: page_ops };
74+
let content = Content {
75+
operations: page_ops,
76+
};
7577
let content_stream = Stream::new(dictionary! {}, content.encode()?);
7678
let content_id = doc.add_object(content_stream);
77-
79+
7880
let mut xobject_dict = lopdf::Dictionary::new();
7981
for (name, obj_ref) in all_xobjects {
8082
xobject_dict.set(name, obj_ref);
8183
}
82-
84+
8385
let page_resources = dictionary! {
8486
"XObject" => xobject_dict,
8587
};
86-
88+
8789
// Create the page
8890
let page_id = doc.add_object(dictionary! {
8991
"Type" => "Page",
@@ -92,25 +94,26 @@ fn embed_pdf_example() -> Result<(), Box<dyn std::error::Error>> {
9294
"Contents" => content_id,
9395
"Resources" => page_resources,
9496
});
95-
97+
9698
// Finalize document structure
97-
let pages_dict = doc.get_object_mut(pages_id)
99+
let pages_dict = doc
100+
.get_object_mut(pages_id)
98101
.and_then(Object::as_dict_mut)
99102
.unwrap();
100103
pages_dict.set("Kids", vec![Object::Reference(page_id)]);
101-
104+
102105
let catalog_id = doc.add_object(dictionary! {
103106
"Type" => "Catalog",
104107
"Pages" => Object::Reference(pages_id),
105108
});
106109
doc.trailer.set("Root", Object::Reference(catalog_id));
107-
110+
108111
// Save the resulting PDF
109112
doc.save("output_with_embedded_pdfs.pdf")?;
110-
113+
111114
Ok(())
112115
}
113116

114117
fn main() -> Result<(), Box<dyn std::error::Error>> {
115118
embed_pdf_example()
116-
}
119+
}

0 commit comments

Comments
 (0)