mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-08 19:42:05 +00:00
fix(pdf): drop an oversized hyperlink before it is copied once per line
Every linked line copied the destination string, and its length was only checked when the annotation was written, so one multi-megabyte link on text wrapping onto many lines was copied that many times first. The length is checked where the link areas are collected.
This commit is contained in:
1 parent
02c38a6d95
commit
5bbab0fa18
2 files changed
+20
-1
No files matched your search
@@ -284,3 +284,20 @@ fn depth_samples() {
|
||||
std::fs::write(format!("{stem}-{name}.png"), figcraft_render::encode_png(&pix).unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_oversized_link_is_dropped_before_it_is_copied_per_line() {
|
||||
// One link far past MAX_URL on text that wraps onto many lines: no annotation, and the
|
||||
// destination is never copied once per line on the way to being refused.
|
||||
let mut d = Document::new("long link");
|
||||
let p = d.first_page().unwrap_or(NodeId(0));
|
||||
let f = d.create(p, NodeKind::Frame, "F", 0.0, 0.0, 300.0, 4000.0).unwrap();
|
||||
let words = "word ".repeat(2_000);
|
||||
let mut td = TextData::new(words.trim_end());
|
||||
td.style.font_size = 20.0;
|
||||
td.style.link = Some("https://example.com/".to_string() + &"a".repeat(1 << 20));
|
||||
let t = d.create(f, NodeKind::Text(td), "T", 10.0, 10.0, 280.0, 3980.0).unwrap();
|
||||
d.get_mut(t).unwrap().fills = vec![Paint::solid(Color::BLACK)];
|
||||
let pdf = export_pdf(&d, &[f], &PdfOptions::default()).unwrap();
|
||||
assert_eq!(text(&pdf).matches("/Subtype/Link").count(), 0);
|
||||
}
|
||||
@@ -111,7 +111,9 @@ fn record_links(ex: &mut Exporter, layout: &TextLayout, gxf: Affine) {
|
||||
let glyphs = layout.glyphs.get(line.glyph_start..line.glyph_end).unwrap_or(&[]);
|
||||
for g in glyphs.iter().map(Some).chain([None]) {
|
||||
let link = g.and_then(|g| {
|
||||
let url = layout.styles.get(g.style)?.link.as_deref().filter(|u| !u.trim().is_empty())?;
|
||||
// Checked here, not when the annotation is written: every linked line copies
|
||||
// the destination, so one oversized link must not be copied at all.
|
||||
let url = layout.styles.get(g.style)?.link.as_deref().map(str::trim).filter(|u| !u.is_empty() && u.len() <= crate::MAX_URL)?;
|
||||
let (a, b) = (g.x, g.x + g.advance);
|
||||
(a.is_finite() && b.is_finite()).then(|| (url, a.min(b), a.max(b)))
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user