Merge branch 'rb-549' into batch/t6

This commit is contained in:
bflatastic
2026-10-16 02:36:55 +07:00
4 changed files with 46 additions and 1 deletions
+4
View File
@@ -1552,6 +1552,10 @@ impl<'d> Exporter<'d> {
.map(|(st, id)| {
let mut ef: Fields = vec![("styleID", u(id))];
style_fields(st, &mut ef);
// An empty link clears the text's own (a missing one would inherit it).
if st.link.is_none() && td.style.link.is_some() {
ef.push(("hyperlink", o(vec![("url", s(""))])));
}
if let Some(p) = st.fills.as_deref().or(td.style.fills.as_ref().map(|_| fills)) {
ef.push(("fillPaints", self.paints(p, true, &Bound::default())));
}
+2 -1
View File
@@ -1762,7 +1762,8 @@ fn text_style(v: &View, size: Option<f64>) -> Obj {
}
}
}
if let Some(u) = v.get("hyperlink").and_then(|h| h.get("url")).and_then(Kv::str).filter(|u| !u.is_empty()) {
// An empty URL is kept: on an override entry it clears the text's own link.
if let Some(u) = v.get("hyperlink").and_then(|h| h.get("url")).and_then(Kv::str) {
s.insert("hyperlink".into(), json!({"type": "URL", "url": u}));
}
s
+2
View File
@@ -130,6 +130,8 @@ fn type_style(cx: &mut Ctx, s: &Value, base: &TextStyle) -> TextStyle {
// and clipping a URL changed it into a different, working-looking one without a word
// (#459). The FIG importer already keeps it whole.
(Some("URL") | None, Some(url)) if !url.is_empty() => t.link = Some(url.to_string()),
// An empty URL clears the link the base style has.
(Some("URL") | None, Some(_)) => t.link = None,
_ => {}
}
}
@@ -0,0 +1,38 @@
//! A hyperlink cleared on part of a text layer stays cleared through FIG export and import.
use figcraft_doc::{Document, NodeKind, TextData};
use figcraft_format::fig;
fn links(doc: &Document, name: &str) -> Vec<Option<String>> {
let Some(NodeKind::Text(text)) = doc.nodes().find(|n| n.name == name).map(|n| &n.kind) else { return Vec::new() };
text.spans().into_iter().flat_map(|(start, end, style)| std::iter::repeat_n(style.link.clone(), end - start)).collect()
}
#[test]
fn cleared_range_hyperlink_round_trip() {
let mut document = Document::new("File");
let page = document.first_page().unwrap();
let url = "https://example.com/".to_string();
let mut text = TextData { characters: "AB".into(), ..Default::default() };
text.style.link = Some(url.clone());
text.restyle(1, 2, |style| style.link = None);
document.create(page, NodeKind::Text(text), "Cleared", 0.0, 0.0, 100.0, 30.0).unwrap();
let mut control = TextData { characters: "AB".into(), ..Default::default() };
control.restyle(1, 2, |style| style.link = Some(url.clone()));
document.create(page, NodeKind::Text(control), "Explicit", 0.0, 40.0, 100.0, 30.0).unwrap();
let expected = vec![Some(url.clone()), None];
assert_eq!(links(&document, "Cleared"), expected);
let native_bytes = figcraft_format::save_bytes(&document, None).unwrap();
let native = figcraft_format::load_bytes(&native_bytes).unwrap().0;
assert_eq!(links(&native, "Cleared"), expected);
let (bytes, export_warnings) = fig::export(&document, None).unwrap();
let (loaded, import_warnings) = fig::import(&bytes, "File").unwrap();
assert!(export_warnings.is_empty(), "{export_warnings:?}");
assert!(import_warnings.is_empty(), "{import_warnings:?}");
assert_eq!(links(&loaded, "Explicit"), vec![None, Some(url)]);
assert_eq!(links(&loaded, "Cleared"), expected);
}