mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-11 04:55:50 +00:00
M12: links in screen-only and print-only XFA text work only where the text shows, and their pieces still join
This commit is contained in:
+17
-1
@@ -748,12 +748,14 @@ impl Emitter<'_> {
|
||||
content.extend(format!("/OC /{resource} BDC\n").bytes());
|
||||
}
|
||||
open = group;
|
||||
// A link's pieces join only within one group: what is in another shows elsewhere.
|
||||
last_link = None;
|
||||
}
|
||||
// A link's pieces join only across text and the underline drawn just before it: a fill,
|
||||
// picture, field or any other line (a rule) between them ends the link there.
|
||||
let keeps_link = match item {
|
||||
Item::Text(_) => true,
|
||||
Item::Line { from, to, .. } => matches!(page.items.get(i + 1), Some(Item::Text(s))
|
||||
Item::Line { from, to, .. } => matches!(page.items.get(i + 1).map(bare), Some(Item::Text(s))
|
||||
if (from.1 - to.1).abs() < 0.01
|
||||
&& (from.0 - s.x).abs() < 0.01
|
||||
&& (from.1 - (s.baseline + s.face.size * crate::layout::UNDERLINE_DROP)).abs() < 0.01),
|
||||
@@ -809,6 +811,12 @@ impl Emitter<'_> {
|
||||
content.extend(literal(&win_ansi(&s.text)));
|
||||
content.extend_from_slice(format!(" Tj{close}\n").as_bytes());
|
||||
if let Some(r) = self.text_link(s, h, page_ref, &mut last_link)? {
|
||||
// A link works only where its text shows: one in print-only text is
|
||||
// NoView (32), so it can't be clicked on screen, and one in screen-only
|
||||
// text doesn't print, as a field's widget there.
|
||||
if let Some(g) = group {
|
||||
self.doc.update_dict(r, |d| d.set(b"F".to_vec(), Object::Int(if g == 0 { 0 } else { 4 | 32 })))?;
|
||||
}
|
||||
annots.push(Object::Ref(r));
|
||||
}
|
||||
}
|
||||
@@ -921,6 +929,14 @@ impl Emitter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `item`, or what it holds when it is shown only on screen or only in print ([`Item::Only`]).
|
||||
fn bare(item: &Item) -> &Item {
|
||||
match item {
|
||||
Item::Only { item, .. } => item,
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
/// The link annotation a page's text last ended in: the next piece of the same link on the same
|
||||
/// line widens it.
|
||||
struct LinkPiece {
|
||||
|
||||
@@ -870,6 +870,48 @@ fn links_to_anything_but_web_and_email_addresses_stay_plain_text() {
|
||||
assert!(warned[0].contains("http, https") && warned[0].contains("mailto"), "{warned:?}");
|
||||
}
|
||||
|
||||
/// A link in text shown only on screen or only in print works only where its text shows: in
|
||||
/// print-only text it is NoView (`/F 36`), so it can't be clicked on screen over nothing, and in
|
||||
/// screen-only text it doesn't print (`/F 0`). Its pieces still join into one link across a change
|
||||
/// of style and the underline drawn with them.
|
||||
#[test]
|
||||
fn links_in_screen_only_and_print_only_text_work_where_their_text_shows() {
|
||||
let draw = |name: &str, attrs: &str| {
|
||||
format!(
|
||||
r#"<draw name="{name}" w="4in" {attrs}><font typeface="Arial" size="10pt"/><value><exData contentType="text/html"><body xmlns="http://www.w3.org/1999/xhtml"><p style="text-decoration:underline"><a href="https://example.com/{name}">forms@<b>example</b>.com</a></p></body></exData></value></draw>"#
|
||||
)
|
||||
};
|
||||
let xdp = format!(
|
||||
r##"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"><template xmlns="http://www.xfa.org/schema/xfa-template/3.3/"><subform name="form" layout="tb">
|
||||
<pageSet><pageArea name="page"><contentArea x="0.5in" y="0.5in" w="7.5in" h="10in"/><medium short="8.5in" long="11in"/></pageArea></pageSet>
|
||||
{}{}{}
|
||||
</subform></template></xdp:xdp>"##,
|
||||
draw("everywhere", ""),
|
||||
draw("screen", r#"relevant="-print""#),
|
||||
draw("paper", r#"presence="invisible" relevant="+print""#),
|
||||
);
|
||||
let mut doc = Document::open(Arc::new(shell(&xdp))).unwrap();
|
||||
render_into(&mut doc).unwrap();
|
||||
// Each link annotation's address and flags.
|
||||
let catalog = doc.get(doc.root().unwrap());
|
||||
let pages = doc.resolve(catalog.as_dict().unwrap().get(b"Pages").unwrap());
|
||||
let kids = doc.resolve(pages.as_dict().unwrap().get(b"Kids").unwrap()).as_array().unwrap().clone();
|
||||
let page = doc.resolve(kids.first().unwrap());
|
||||
let annots = doc.resolve(page.as_dict().unwrap().get(b"Annots").unwrap()).as_array().unwrap().clone();
|
||||
let links: Vec<(String, i64)> = annots
|
||||
.iter()
|
||||
.map(|a| doc.resolve(a).as_dict().unwrap().clone())
|
||||
.filter(|d| d.name(b"Subtype") == Some(&b"Link"[..]))
|
||||
.map(|d| {
|
||||
let action = doc.resolve(d.get(b"A").unwrap()).as_dict().unwrap().clone();
|
||||
(String::from_utf8_lossy(&action.get(b"URI").and_then(Object::as_string).unwrap().bytes).into_owned(), d.int(b"F").unwrap())
|
||||
})
|
||||
.collect();
|
||||
let expected = [("https://example.com/everywhere", 4), ("https://example.com/screen", 0), ("https://example.com/paper", 36)];
|
||||
assert_eq!(links, expected.map(|(u, f)| (u.to_string(), f)), "one link each, with flags where its text shows");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn documents_without_xfa_are_not_touched() {
|
||||
let mut doc = Document::new_empty();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user