Skip to content

Commit ddb0748

Browse files
committed
perf: cache processed text width
This avoids unnecessarily processing the additional text for every page
1 parent a085bcb commit ddb0748

File tree

4 files changed

+44
-29
lines changed

4 files changed

+44
-29
lines changed

crates/c2pdf/src/lib/code_to_pdf.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use syntect::{
2222
parsing::SyntaxSet,
2323
};
2424

25-
use crate::{dimensions::Dimensions, helpers::init_page, text_manipulation::TextWrapper};
25+
use crate::{dimensions::Dimensions, helpers::{init_page, ProcessedText}, text_manipulation::TextWrapper};
2626

2727
/// Configuration struct for the highlighter ([`syntect`])
2828
///
@@ -103,7 +103,7 @@ pub struct CodeToPdf {
103103
text_wrapper: TextWrapper,
104104
processed_file_count: usize,
105105
// Text to put at the top of every page
106-
page_text: Option<String>,
106+
page_text: Option<ProcessedText>,
107107
}
108108
impl CodeToPdf {
109109
/// Initialises a new [`CodeToPdf`]
@@ -112,7 +112,7 @@ impl CodeToPdf {
112112
font_id: FontId,
113113
page_dimensions: Dimensions,
114114
text_wrapper: TextWrapper,
115-
page_text: Option<String>,
115+
page_text: Option<ProcessedText>,
116116
) -> Self {
117117
Self {
118118
current_page_contents: vec![],
@@ -149,7 +149,7 @@ impl CodeToPdf {
149149
self.font_id.clone(),
150150
self.text_wrapper.font_size(),
151151
path,
152-
self.page_text.as_deref(),
152+
self.page_text.as_ref(),
153153
&mut self.text_wrapper,
154154
);
155155
}

crates/c2pdf/src/lib/helpers.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
11
//! Miscellaneous helper functions
22
3-
use std::path::Path;
3+
use std::{path::Path, str::Lines};
44

55
use printpdf::{FontId, Mm, Op, Point, Pt, TextItem, TextMatrix, TextRenderingMode};
66

77
use crate::{dimensions::Dimensions, text_manipulation::TextWrapper};
8-
8+
/// Processed additional text.
9+
///
10+
/// Pretty much just serves to cache the maximum line width within the text so it doesn't have to be recalculated
11+
#[derive(Clone)]
12+
pub struct ProcessedText {
13+
text: String,
14+
width: f32,
15+
}
16+
impl ProcessedText {
17+
/// Creates a new instance of [`ProcessedText`]
18+
pub fn new(text: String, wrapper: &mut TextWrapper) -> Option<Self> {
19+
let width = text
20+
.lines()
21+
.map(|line| wrapper.get_width(line).0)
22+
.reduce(f32::max)?;
23+
Some(Self { text, width })
24+
}
25+
fn lines(&self) -> Lines {
26+
self.text.lines()
27+
}
28+
}
929
/// Generates a new page with basic contents
1030
pub fn init_page(
1131
contents: &mut Vec<Op>,
1232
page_dimensions: &Dimensions,
1333
font_id: FontId,
1434
font_size: f32,
1535
path: &Path,
16-
additional_text: Option<&str>,
36+
additional_text: Option<&ProcessedText>,
1737
wrapper: &mut TextWrapper,
1838
) {
1939
contents.extend_from_slice(&[
@@ -25,23 +45,15 @@ pub fn init_page(
2545
font: font_id.clone(),
2646
},
2747
]);
28-
let mut additional_text_width = 0.0;
2948
// Write additional text
30-
if let Some(text) = additional_text {
31-
for line in text.lines() {
32-
let line_width = wrapper.get_width(line).0;
33-
if line_width > additional_text_width {
34-
additional_text_width = line_width
35-
}
36-
}
49+
if let Some(ref text) = additional_text {
3750
contents.extend_from_slice(&[
3851
Op::SetTextMatrix {
3952
matrix: TextMatrix::Translate(Pt(0.0), Pt(0.0)),
4053
},
4154
Op::SetTextCursor {
4255
pos: Point {
43-
x: (page_dimensions.width - page_dimensions.margin_right).into_pt()
44-
- Pt(additional_text_width),
56+
x: (page_dimensions.width - page_dimensions.margin_right).into_pt() - Pt(text.width),
4557
y: (page_dimensions.height - Mm(7.5)).into(),
4658
},
4759
},
@@ -66,15 +78,14 @@ pub fn init_page(
6678
},
6779
},
6880
]);
69-
for (line, _) in wrapper.split_into_lines(&path.display().to_string(), |_| {
70-
(page_dimensions.max_text_width()
71-
- if additional_text.is_some() {
72-
Mm::from(Pt(additional_text_width)) + Mm(5.0)
73-
} else {
74-
Mm(0.0)
75-
})
76-
.into_pt()
77-
}) {
81+
let max_path_width = (page_dimensions.max_text_width()
82+
- if let Some(text) = &additional_text {
83+
Mm::from(Pt(text.width)) + Mm(5.0)
84+
} else {
85+
Mm(0.0)
86+
})
87+
.into_pt();
88+
for (line, _) in wrapper.split_into_lines(&path.display().to_string(), |_| max_path_width) {
7889
contents.push(Op::WriteText {
7990
items: vec![TextItem::Text(line)],
8091
font: font_id.clone(),

crates/c2pdf/src/lib/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::{
1515

1616
use code_to_pdf::{CodeToPdf, DocumentSubset, HighlighterConfig};
1717
use dimensions::Dimensions;
18+
use helpers::ProcessedText;
1819
use ignore::{WalkBuilder, overrides::OverrideBuilder};
1920
use logging::Logger;
2021
use printpdf::FontId;
@@ -78,21 +79,23 @@ impl CodeToPdf {
7879

7980
let doc_subset = Arc::new(Mutex::new(doc_subset));
8081
if let Some(threads) = threads {
81-
// Build the global threadpool with the correct number of threads
82+
// Build the global threadpool with the correct number of threads
8283
rayon::ThreadPoolBuilder::new()
8384
.num_threads(u8::from(threads) as usize)
8485
.build_global()
8586
.unwrap();
8687
}
88+
let mut wrapper = TextWrapper::new(font_bytes, font_size);
89+
let additional_text = page_text.and_then(|text| ProcessedText::new(text, &mut wrapper));
8790
walker.enumerate().par_bridge().for_each(|(i, result)| {
8891
// let mut doc = PdfDocument::new(&args.name);
8992
let c2pdf_mutex = local_c2pdf.get_or(|| {
9093
Arc::new(Mutex::new(CodeToPdf::new(
9194
doc_subset.clone(),
9295
font_id.clone(),
9396
page_dimensions.clone(),
94-
TextWrapper::new(font_bytes, font_size),
95-
page_text.clone(),
97+
wrapper.clone(),
98+
additional_text.clone(),
9699
)))
97100
});
98101
let highlight_config_mutex = local_highlighter_config.get_or(|| {

crates/c2pdf/src/lib/text_manipulation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub fn split_into_lines_fontdue<F: Fn(usize) -> Pt>(
4747
}
4848

4949
/// Handles wrapping text into multiple lines
50+
#[derive(Clone)]
5051
pub struct TextWrapper {
5152
rasterize_cache: HashMap<char, f32>,
5253
font: Font,

0 commit comments

Comments
 (0)