From 01d0dc67baa9450ad6b0abfdb50d3e9dd54d9b89 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 2 Oct 2026 09:59:28 -0400 Subject: [PATCH] M5: Fill & Sign typed signatures and initials (Type or Draw; script-font outlines drawn as filled paths); fill_sign_add signature/initials Co-Authored-By: Claude Opus 5.5 --- ATTRIBUTION.md | 3 +- ATTRIBUTION.toml | 11 ++ Cargo.lock | 4 + NOTICE | 1 + assets/fonts/DancingScript.ttf | Bin 0 -> 133636 bytes assets/fonts/OFL-DancingScript.txt | 93 ++++++++++++++++ crates/annot/src/appearance.rs | 17 +++ crates/annot/src/lib.rs | 17 ++- crates/automation/src/comments.rs | 8 ++ crates/automation/src/tools.rs | 4 +- crates/automation/tests/automation.rs | 7 +- crates/engine/Cargo.toml | 1 + crates/engine/src/catalog.rs | 1 + crates/engine/src/commands.rs | 1 + crates/engine/src/lib.rs | 17 ++- crates/fonts/Cargo.toml | 2 + crates/fonts/src/lib.rs | 3 + crates/fonts/src/script.rs | 109 +++++++++++++++++++ crates/ui-egui/src/canvas.rs | 9 +- crates/ui-egui/src/commands.rs | 5 +- crates/ui-egui/src/dialogs.rs | 13 ++- crates/ui-egui/src/fill_sign.rs | 147 ++++++++++++++++++++++++-- crates/ui-egui/src/lib.rs | 27 +++-- crates/ui-egui/tests/fill_sign.rs | 33 ++++++ parity/acrobat-features.toml | 8 +- 25 files changed, 506 insertions(+), 35 deletions(-) create mode 100644 assets/fonts/DancingScript.ttf create mode 100644 assets/fonts/OFL-DancingScript.txt create mode 100644 crates/fonts/src/script.rs diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index 6b3a38a7e..a05f8422a 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -4,7 +4,7 @@ Every asset PrintCraft includes, bundles or uses to build its published material, with its author, source and licence. The policy is in [AGENTS.md](AGENTS.md) §1. The machine-readable list, with SHA-256 hashes, is [ATTRIBUTION.toml](ATTRIBUTION.toml). Licence texts are kept beside the assets and summarised in [NOTICE](NOTICE). -## In this repository (237) +## In this repository (238) | Asset | Title | Author | Licence | Source | Used for | |---|---|---|---|---|---| @@ -245,6 +245,7 @@ Every asset PrintCraft includes, bundles or uses to build its published material | `assets/icons/message-square-quote.svg` | Lucide icon "message-square-quote" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/message-square-quote.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs | | `crates/sign/tests/data/openssl-signed.pdf` | One-page PDF signed with OpenSSL CMS (adbe.pkcs7.detached) by the RSA test key | PrintCraft contributors | MIT OR Apache-2.0 | Contributor-original: hand-written PDF objects; signature made with `openssl cms -sign` over its byte ranges (see crates/sign/tests/data/README.md) | Signature validation tests (crates/sign/tests/pdf.rs): a signature PrintCraft did not make | | `assets/icons/clipboard-paste.svg` | Lucide icon "clipboard-paste" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/clipboard-paste.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs | +| `assets/fonts/DancingScript.ttf` | Dancing Script (variable) | The Dancing Script Project Authors (Pablo Impallari) | OFL-1.1 | https://github.com/google/fonts/blob/9710da1eacb3be272583c3224dcb70f9da6eadbb/ofl/dancingscript/DancingScript%5Bwght%5D.ttf | Fill & Sign typed signatures and initials: the typed name's glyph outlines, drawn as filled paths (the font file itself is not embedded in PDFs) | ## Compiled in through dependencies (6) diff --git a/ATTRIBUTION.toml b/ATTRIBUTION.toml index fd107792e..92a0df3f9 100644 --- a/ATTRIBUTION.toml +++ b/ATTRIBUTION.toml @@ -2948,3 +2948,14 @@ licence_file = "assets/icons/LICENSE-lucide.txt" kind = "icon" usage = "UI icon, embedded via crates/ui-egui/src/icon_data.rs" sha256 = "631d24bde604be36b2dcf3b6f86ddf6026879cdcd272c0dfe37ab0cc6b341fda" + +[[asset]] +path = "assets/fonts/DancingScript.ttf" +title = "Dancing Script (variable)" +author = "The Dancing Script Project Authors (Pablo Impallari)" +source = "https://github.com/google/fonts/blob/9710da1eacb3be272583c3224dcb70f9da6eadbb/ofl/dancingscript/DancingScript%5Bwght%5D.ttf" +licence = "OFL-1.1" +licence_file = "assets/fonts/OFL-DancingScript.txt" +kind = "font" +usage = "Fill & Sign typed signatures and initials: the typed name's glyph outlines, drawn as filled paths (the font file itself is not embedded in PDFs)" +sha256 = "21808625578fe8d8cd10cb684be546dca077b27cd03a53a2f1ec11dc743c924c" diff --git a/Cargo.lock b/Cargo.lock index 8f939d711..52f41aff4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3460,6 +3460,7 @@ dependencies = [ "printcraft-cos", "printcraft-create", "printcraft-edit", + "printcraft-fonts", "printcraft-forms", "printcraft-model", "printcraft-optimize", @@ -3485,6 +3486,9 @@ dependencies = [ [[package]] name = "printcraft-fonts" version = "0.1.0" +dependencies = [ + "skrifa 0.44.0", +] [[package]] name = "printcraft-forms" diff --git a/NOTICE b/NOTICE index 466cd63a7..cafc3a17f 100644 --- a/NOTICE +++ b/NOTICE @@ -8,6 +8,7 @@ policy is AGENTS.md §1. - Lucide icons (assets/icons/*.svg) — ISC License. See assets/icons/LICENSE-lucide.txt. - Inter typeface (assets/fonts/Inter-*.ttf) — SIL Open Font License 1.1. See assets/fonts/OFL-Inter.txt. - JetBrains Mono typeface (assets/fonts/JetBrainsMono-*.ttf) — SIL Open Font License 1.1. See assets/fonts/OFL-JetBrainsMono.txt. +- Dancing Script typeface (assets/fonts/DancingScript.ttf), Copyright 2016 The Dancing Script Project Authors — SIL Open Font License 1.1 (Reserved Font Name "Dancing Script"). See assets/fonts/OFL-DancingScript.txt. - hayro (rendering, via crates.io) — Apache-2.0 OR MIT. Its `embed-fonts` feature bundles the Foxit standard-14 Type 1 fonts under BSD-3-Clause ("Copyright 2014 PDFium Authors"), and hayro-cmap bundles Adobe CMap resources under BSD-3-Clause. diff --git a/assets/fonts/DancingScript.ttf b/assets/fonts/DancingScript.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9f521e7fb357ec9ea9820ebd36a9d7b1f72f2935 GIT binary patch literal 133636 zcmdSC2b^R@wJ=_FZ|9tI&N-*ase8iC%+AbC4m&%`CN{Adc9&&=B_mlRC_X`gf&tNo z2~{`j z`P%7H?-%Xed~`qaKJqO7Jk)oc zwR!Kh-lHn$+mmo#@VEW@4jq2vROfFI`l$>dtz!SdZTrs@Ty+UN58sgxC<#rY-pQv< zwQf?j{}r+93jje+sxi5bem-;OLsj+Z-|#g&LU#ew<^I8Q>@BCEF7K~Zb(PQ8(3Wa9 z$%VOyK;ZA)@RkSRN%E%Ee?0ik!+*xtAwJZ)k^zj-Fn7hvD1*MaS{0$&5jwvc?$=@K z$JSTSBM800)NmNY8KA&>$$dP+JPsEZV)4h}8xL9^ufEKzAT47+2Igv1!mn2UjDLlC znFxH}iHi8U)o1Vrs?Ra&k%MWiewKLy>R*5o%s!-Nlt{xoh`dY>QZc6LKk*+>h4~sX zGe?n`y%MfBL3uwiGUrr(hyT6$JifB}I_|5!fuF9vf=8>b;!^ctd`a~s{4=1ypQ~@6 zXR6)!%fQJmA(pwc`g8mQyn8P^b6NGn_8qpm}K>j_%ssWUsV5&e_efxNuxB-pbc=gGX*q*dDR!OsQLhY5bePqs{Q~y zT74b;7>UqNkOV!2Fr$FaKSBZg8{|Xhp^MO8pwXHA1IVZ z2%j{UU8qR-hTRThM|kJ|wA6TweF}Ju@LN-1b@08d`dOX<_>b^lz1)b#*>$z=gfIU; zF02zI@SDg<=K+}u%?0KKnHy`$W|(giD287|ZDh_saX01;nLqzSh0MX`LgylxkN;JH zc}nIgov+_OA@=_2t;{a~pZ`vIH=O^A=cp_o^5FkbVVu?HNujca$eZRuWDhCNR9|5Y zH916N(f>rb8-=JGA~LPH{2b&Ek#Vnrj3aW6TwkeO$!8A!WaA!u$KP+jx%)q@b|WZ6 zKH{sY&kz~OmFj6626_G%eE%a{iOj5mOd?lOzEypO`FQO!k)>(}XrrcJ2 zm5Ej#X8fS{o(3KF=jv-~%B%4GpVc>60qC{Ykc52<`gA_piH&GC=#*Z(1r6dLn!s{Y z#+vHo%okCXIfdfPC(tCm3>{`t=q~gs3Ng1KFsRY#P)-Jz8jhGZ;==OJMiH* zQ5M_bnu06ne^MADGJtN(ft_JszJMkfEsV`G)!#DrL*K5bs)4uEOcd^ofer8;&}-X~ zfcY7;dm&2j?gSg)6Hw-=KVr^9KA3gx`@fyTqhN?e-ey(SK3T+Pn%`S##Zbw39 z9F5TuM^|GpTEZ+m&x3Lv8o+m=0Q)3Lpzji!r}`!MBZBKQaJ9qrc>;wi*u~YK)XMMR zdN&keSF&)u7YcnEu5Utl5Xw3z&{p*dC}2-kr=jeDG71H_t9lyB{qVdNuJDfP*PuKK zrT&f^;Pczs{}*kK!2G%ywS&zuhlk-xFnSv4==Ejz{4;tF;TF_R>s8@;FFFP^g@29r zlJ^r_n#&_ZzOZLfnvlxGa)o|F+bklss1EV?pN8w@=mL;iKs_W!Nz{V6(E?gQ$I(fA z5?_mNz<1zJ;qT$!;TIVz6KAqak(p;6X1>XMoB27*V`Z$8)v#99$A;Jhn`ZNlOC0~^ z_=ij9vbY>Bk1OCxxmsOi*J0NkuDe_xaoy|sgzM9;2VGxueaUTbdpvxP#-sO`J%gSV z&$c(1Y8CJxn7B|1wW1!hh&H2h(d`tI+wuMQQT#mq1O6uyVNx|r9s^8%!Xj1xn5Y30 zFB_;~asgm+yG!dbxoj>M#iXT%$(^oy0F#fo9so?f;ClESm~4Fm1T1q3{}pf%0A0Td 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 Option { } } b"Stamp" => { + // A typed signature: filled outlines normalised to the rectangle. + if let Some(outline) = d.get(b"PCOutline").and_then(|o| o.as_array()) { + let [x0, y0, x1, y1] = rect; + let (w, h) = (x1 - x0, y1 - y0); + c.push_str(&rg(stroke.unwrap_or([0.0; 3]))); + for contour in outline { + let v: Vec = contour.as_array().map(|a| a.iter().filter_map(Object::as_f64).collect()).unwrap_or_default(); + for (i, p) in v.chunks_exact(2).enumerate() { + c.push_str(&format!("{} {} {}\n", n(x0 + p[0] * w), n(y0 + p[1] * h), if i == 0 { "m" } else { "l" })); + } + if v.len() >= 6 { + c.push_str("h\n"); + } + } + c.push_str("f*\n"); + return Some(form(rect, c.as_bytes(), res)); + } // A custom stamp: its picture (an image, or a form mapped to /PCPictureSize) fills // the rectangle. if let Some(pic) = d.get(b"PCPicture").and_then(Object::as_ref) { diff --git a/crates/annot/src/lib.rs b/crates/annot/src/lib.rs index f8ce3cbae..4132daf31 100644 --- a/crates/annot/src/lib.rs +++ b/crates/annot/src/lib.rs @@ -327,6 +327,12 @@ pub enum Shape { stamp: StampKind, by: Option, }, + /// A typed signature or initials: filled outlines (each contour as points normalised to + /// `rect`, 0–1, y up), drawn with the even-odd rule. + TypedSignature { + rect: [f64; 4], + contours: Vec>, + }, /// A custom stamp: a picture already in the document (an image XObject, or a form XObject /// whose `/Matrix` maps it to `size` points) filling `rect`, named `name`. CustomStamp { @@ -417,7 +423,7 @@ impl Shape { Shape::Line { .. } => "Line", Shape::Ink { .. } | Shape::Signature { .. } => "Ink", Shape::TextBox { .. } | Shape::Typewriter { .. } | Shape::Callout { .. } => "FreeText", - Shape::Mark { .. } | Shape::Stamp { .. } | Shape::CustomStamp { .. } => "Stamp", + Shape::Mark { .. } | Shape::Stamp { .. } | Shape::CustomStamp { .. } | Shape::TypedSignature { .. } => "Stamp", Shape::Redact { .. } => "Redact", Shape::Polygon { .. } => "Polygon", Shape::PolyLine { .. } => "PolyLine", @@ -523,6 +529,7 @@ impl Style { Shape::Signature { .. } => ([0.0, 0.0, 0.0], 1.5), Shape::Stamp { stamp, .. } => (stamp.color(), 2.0), Shape::CustomStamp { .. } => ([0.0, 0.0, 0.0], 0.0), + Shape::TypedSignature { .. } => ([0.0, 0.0, 0.0], 0.0), // Red outline while marked; a black box once applied. Shape::Redact { .. } => return Self { color: [0.89, 0.13, 0.13], opacity: 1.0, width: 1.0, fill: Some([0.0, 0.0, 0.0]) }, }; @@ -715,6 +722,7 @@ fn rect_for(shape: &Shape, style: &Style) -> Result<[f64; 4], AnnotError> { | Shape::Typewriter { rect, .. } | Shape::Stamp { rect, .. } | Shape::CustomStamp { rect, .. } + | Shape::TypedSignature { rect, .. } | Shape::Mark { rect, .. } => { let r = normalize(*rect); if !finite(rect) || r[2] - r[0] < 1.0 || r[3] - r[1] < 1.0 { @@ -811,6 +819,7 @@ fn subject(shape: &Shape) -> &'static str { StampGroup::StandardBusiness => "Stamp", }, Shape::CustomStamp { .. } => "Stamp", + Shape::TypedSignature { .. } => "Signature", Shape::Polygon { cloud: true, .. } => "Cloud", Shape::Polygon { .. } => "Polygon", Shape::PolyLine { .. } => "Polygonal Line", @@ -916,6 +925,12 @@ pub fn add_annotation(doc: &mut Document, new: &NewAnnotation, meta: &Meta) -> R d.set(b"PCByLine".to_vec(), PdfString::text(b)); } } + Shape::TypedSignature { contours, .. } => { + d.set(b"C".to_vec(), rgb(style.color)); + d.set(b"Name".to_vec(), Object::name("PCTypedSignature")); + let list = contours.iter().filter(|c| c.len() > 2).map(|c| num_array(&c.iter().flat_map(|p| [p[0], p[1]]).collect::>())).collect(); + d.set(b"PCOutline".to_vec(), Object::Array(list)); + } Shape::CustomStamp { name, picture, image, size, .. } => { let clean: String = name.chars().filter(|c| c.is_ascii_alphanumeric()).collect(); d.set(b"Name".to_vec(), Object::name(&format!("PCCustom{clean}"))); diff --git a/crates/automation/src/comments.rs b/crates/automation/src/comments.rs index eb338ddfd..0f41bdbb6 100644 --- a/crates/automation/src/comments.rs +++ b/crates/automation/src/comments.rs @@ -352,6 +352,14 @@ impl Automation { let t = format!("{m}/{d}/{yy}"); (text_at(&t), t) } + // A typed signature or initials in the script font, left edge at `at`. + kind @ ("signature" | "initials") => { + let t = a.str("text")?; + let h = if kind == "initials" { 24.0 } else { 32.0 }; + let shape = + printcraft_engine::typed_signature_shape(at, t, h).ok_or_else(|| ToolError::InvalidArgs("text has nothing to draw".into()))?; + (shape, String::new()) + } kind => { let mark = match kind { "check" => FillMark::Check, diff --git a/crates/automation/src/tools.rs b/crates/automation/src/tools.rs index 636210442..e284939ea 100644 --- a/crates/automation/src/tools.rs +++ b/crates/automation/src/tools.rs @@ -754,14 +754,14 @@ pub fn tools() -> Vec { t( "fill_sign_add", "Fill & Sign: type text or place a mark", - "Fill in a form that has no fields, as Acrobat's Fill & Sign does: type text (`text`, 10 pt), place a check, cross, dot or line, or today's date, at `at` [x, y] in points from the top-left of the page (the text's top-left; a mark's centre). Creates movable, undoable annotations.", + "Fill in a form that has no fields, as Acrobat's Fill & Sign does: type text (`text`, 10 pt), place a check, cross, dot or line, today's date, or a typed signature or initials (`text` drawn in a script font as filled outlines; at is its left edge, centred vertically), at `at` [x, y] in points from the top-left of the page (the text's top-left; a mark's centre). Creates movable, undoable annotations.", ) .cmd("sign.fill.text") .with(schema( json!({ "doc": doc(), "page": { "type": "integer", "minimum": 1 }, - "type": { "type": "string", "enum": ["text", "check", "cross", "dot", "line", "date"] }, + "type": { "type": "string", "enum": ["text", "check", "cross", "dot", "line", "date", "signature", "initials"] }, "at": point(), "text": { "type": "string", "minLength": 1 }, "author": { "type": "string" }, diff --git a/crates/automation/tests/automation.rs b/crates/automation/tests/automation.rs index 938831022..dcaada256 100644 --- a/crates/automation/tests/automation.rs +++ b/crates/automation/tests/automation.rs @@ -676,10 +676,15 @@ fn fill_and_sign_through_tools() { ok(&mut a, "fill_sign_add", json!({ "doc": doc, "page": 1, "type": "text", "at": [20, 40], "text": "Ada Lovelace" })); ok(&mut a, "fill_sign_add", json!({ "doc": doc, "page": 1, "type": "check", "at": [20, 80] })); ok(&mut a, "fill_sign_add", json!({ "doc": doc, "page": 1, "type": "date", "at": [20, 100] })); + ok(&mut a, "fill_sign_add", json!({ "doc": doc, "page": 1, "type": "signature", "at": [20, 150], "text": "Ada Lovelace" })); + assert!(matches!( + a.call("fill_sign_add", &json!({ "doc": doc, "page": 1, "type": "initials", "at": [20, 190], "text": " " })), + Err(ToolError::InvalidArgs(_)) + )); let texts = page_text(&mut a, doc); assert!(texts[0].contains("Ada Lovelace") && texts[0].contains("11/14/2023"), "{texts:?}"); let list = ok(&mut a, "comment_list", json!({ "doc": doc })); - assert_eq!(list["count"], 3); + assert_eq!(list["count"], 4); assert!(matches!(a.call("fill_sign_add", &json!({ "doc": doc, "page": 1, "type": "text", "at": [1, 1] })), Err(ToolError::InvalidArgs(_)))); } diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 1e888d755..74f5d0c04 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -22,6 +22,7 @@ printcraft-xfdf = { workspace = true } printcraft-sign = { workspace = true } printcraft-optimize = { workspace = true } printcraft-a11y = { workspace = true } +printcraft-fonts = { workspace = true } printcraft-create = { workspace = true } printcraft-model = { workspace = true } thiserror = { workspace = true } diff --git a/crates/engine/src/catalog.rs b/crates/engine/src/catalog.rs index 7ee72d8d0..de02b8645 100644 --- a/crates/engine/src/catalog.rs +++ b/crates/engine/src/catalog.rs @@ -97,6 +97,7 @@ pub static TOOL_GROUPS: &[ToolGroup] = &[ item("Line", "minus", "sign.fill.line", Ready), item("Date", "clock-3", "sign.fill.date", Ready), item("Signature", "signature", "sign.fill.signature", Ready), + item("Initials", "signature", "sign.fill.initials", Ready), ], }], }, diff --git a/crates/engine/src/commands.rs b/crates/engine/src/commands.rs index d2ba98890..91a8e8ce4 100644 --- a/crates/engine/src/commands.rs +++ b/crates/engine/src/commands.rs @@ -242,6 +242,7 @@ pub const COMMANDS: &[CommandSpec] = &[ c("sign.fill.line", "Fill & Sign: line", None, None, Annotate, "minus"), c("sign.fill.date", "Fill & Sign: date", None, None, Annotate, "clock-3"), c("sign.fill.signature", "Fill & Sign: sign", None, None, Annotate, "signature"), + c("sign.fill.initials", "Fill & Sign: initials", None, None, Annotate, "signature"), c("export.image", "Export to image…", FILE, None, Document, "image"), c("optimize.reduce", "Reduce file size…", FILE, None, Document, "file-down"), c("optimize.advanced", "Optimize PDF…", FILE, None, Document, "settings-2"), diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index d24f01493..f26a6ac2c 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -31,6 +31,21 @@ pub use printcraft_forms::{ }; pub use printcraft_a11y as a11y; +pub use printcraft_fonts::{ScriptOutline, script_outline}; + +/// Fill & Sign: `text` in the script font as a typed signature, its left edge at `at` (user +/// space, vertically centred) and `height` points tall. `None` for text with no outlines. +pub fn typed_signature_shape(at: [f64; 2], text: &str, height: f64) -> Option { + let o = script_outline(text); + let span = (o.ascent - o.descent).max(0.1); + if o.contours.is_empty() || o.width <= 0.0 { + return None; + } + let k = height / span; + let rect = [at[0], at[1] - height / 2.0, at[0] + o.width * k, at[1] + height / 2.0]; + let contours = o.contours.iter().map(|c| c.iter().map(|p| [p[0] / o.width, (p[1] - o.descent) / span]).collect()).collect(); + Some(Shape::TypedSignature { rect, contours }) +} /// Comment geometry helpers (text-box line breaking) for frontends. pub use printcraft_annot::appearance as annot_text; pub use printcraft_annot::links::{Highlight as LinkHighlight, LinkAction, LinkItem, LinkStyle}; @@ -933,7 +948,7 @@ fn annotation_noun(s: &Shape) -> &'static str { Shape::Mark { mark: FillMark::Cross, .. } => "cross", Shape::Mark { mark: FillMark::Dot, .. } => "dot", Shape::Mark { mark: FillMark::Line, .. } => "line", - Shape::Signature { .. } => "signature", + Shape::Signature { .. } | Shape::TypedSignature { .. } => "signature", Shape::Redact { .. } => "redaction mark", Shape::Stamp { .. } | Shape::CustomStamp { .. } => "stamp", Shape::Polygon { cloud: true, .. } => "cloud", diff --git a/crates/fonts/Cargo.toml b/crates/fonts/Cargo.toml index 5d9e4424f..c182a22ae 100644 --- a/crates/fonts/Cargo.toml +++ b/crates/fonts/Cargo.toml @@ -9,3 +9,5 @@ license.workspace = true workspace = true [dependencies] +# Glyph outlines of the bundled script font (typed signatures); already in the tree via egui. +skrifa = "0.44" diff --git a/crates/fonts/src/lib.rs b/crates/fonts/src/lib.rs index d40ecc9c9..4048e7219 100644 --- a/crates/fonts/src/lib.rs +++ b/crates/fonts/src/lib.rs @@ -3,6 +3,9 @@ //! See the README: the metrics are approximations by character class (no vendor metrics files //! are bundled). The full font subsystem lands in M2.2/M7. +mod script; +pub use script::{ScriptOutline, script_outline}; + /// Approximate advance of `s` in Helvetica (or Arial) at `size` points. pub fn helvetica_width(s: &str, size: f64) -> f64 { let units: f64 = s diff --git a/crates/fonts/src/script.rs b/crates/fonts/src/script.rs new file mode 100644 index 000000000..36737598c --- /dev/null +++ b/crates/fonts/src/script.rs @@ -0,0 +1,109 @@ +//! Typed signatures: a name in the bundled script font (Dancing Script, OFL-1.1) as glyph +//! outlines, flattened to polygons. Drawn as filled paths, so no font is embedded in the PDF. + +use skrifa::instance::{LocationRef, Size}; +use skrifa::outline::{DrawSettings, OutlinePen}; +use skrifa::{FontRef, MetadataProvider}; + +static FONT: &[u8] = include_bytes!("../../../assets/fonts/DancingScript.ttf"); + +/// Outlines of a line of text at a font size of 1 (em units): y up, the baseline at 0, starting +/// at x = 0. Fill them with the even-odd (or nonzero) rule. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ScriptOutline { + pub contours: Vec>, + pub width: f64, + pub ascent: f64, + pub descent: f64, +} + +struct Flatten { + contours: Vec>, + cur: Vec<[f64; 2]>, + scale: f64, + dx: f64, +} + +impl Flatten { + fn pt(&self, x: f32, y: f32) -> [f64; 2] { + [self.dx + x as f64 * self.scale, y as f64 * self.scale] + } + fn last(&self) -> [f64; 2] { + self.cur.last().copied().unwrap_or([self.dx, 0.0]) + } +} + +const STEPS: usize = 8; + +impl OutlinePen for Flatten { + fn move_to(&mut self, x: f32, y: f32) { + if self.cur.len() > 2 { + self.contours.push(std::mem::take(&mut self.cur)); + } + self.cur.clear(); + let p = self.pt(x, y); + self.cur.push(p); + } + fn line_to(&mut self, x: f32, y: f32) { + let p = self.pt(x, y); + self.cur.push(p); + } + fn quad_to(&mut self, cx: f32, cy: f32, x: f32, y: f32) { + let (p0, c, p) = (self.last(), self.pt(cx, cy), self.pt(x, y)); + for i in 1..=STEPS { + let t = i as f64 / STEPS as f64; + let u = 1.0 - t; + self.cur.push([u * u * p0[0] + 2.0 * u * t * c[0] + t * t * p[0], u * u * p0[1] + 2.0 * u * t * c[1] + t * t * p[1]]); + } + } + fn curve_to(&mut self, c0x: f32, c0y: f32, c1x: f32, c1y: f32, x: f32, y: f32) { + let (p0, c0, c1, p) = (self.last(), self.pt(c0x, c0y), self.pt(c1x, c1y), self.pt(x, y)); + for i in 1..=STEPS { + let t = i as f64 / STEPS as f64; + let u = 1.0 - t; + let (a, b, c, d) = (u * u * u, 3.0 * u * u * t, 3.0 * u * t * t, t * t * t); + self.cur.push([a * p0[0] + b * c0[0] + c * c1[0] + d * p[0], a * p0[1] + b * c0[1] + c * c1[1] + d * p[1]]); + } + } + fn close(&mut self) { + if self.cur.len() > 2 { + self.contours.push(std::mem::take(&mut self.cur)); + } + self.cur.clear(); + } +} + +/// The outlines of `text` in the script font (characters it lacks are skipped). +pub fn script_outline(text: &str) -> ScriptOutline { + let Ok(font) = FontRef::new(FONT) else { return ScriptOutline::default() }; + let loc = LocationRef::default(); + let metrics = font.metrics(Size::unscaled(), loc); + let scale = 1.0 / metrics.units_per_em.max(1) as f64; + let charmap = font.charmap(); + let glyphs = font.outline_glyphs(); + let advances = font.glyph_metrics(Size::unscaled(), loc); + let mut pen = Flatten { contours: Vec::new(), cur: Vec::new(), scale, dx: 0.0 }; + for ch in text.chars() { + let Some(gid) = charmap.map(ch) else { continue }; + if let Some(g) = glyphs.get(gid) { + let _ = g.draw(DrawSettings::unhinted(Size::unscaled(), loc), &mut pen); + pen.close(); + } + pen.dx += advances.advance_width(gid).unwrap_or(0.0) as f64 * scale; + } + ScriptOutline { contours: pen.contours, width: pen.dx, ascent: metrics.ascent as f64 * scale, descent: metrics.descent as f64 * scale } +} + +#[cfg(test)] +mod tests { + #[test] + fn a_name_becomes_outlines() { + let o = super::script_outline("Ada L."); + assert!(o.contours.len() >= 5, "{}", o.contours.len()); + assert!(o.width > 1.0 && o.width < 6.0, "{}", o.width); + assert!(o.ascent > 0.5 && o.descent < 0.0); + let max_x = o.contours.iter().flatten().map(|p| p[0]).fold(0.0, f64::max); + assert!(max_x <= o.width + 0.2); + assert!(super::script_outline("").contours.is_empty()); + } +} diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 050d1c383..446814e07 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -974,6 +974,8 @@ pub fn document_area(app: &mut PrintCraftApp, index: usize, ui: &mut egui::Ui) { let can_crop = doc.allows_assembly(); let mut open_boxes = false; let signature = app.signature.clone(); + let initials = app.initials.clone(); + let mut open_initials = false; let author = app.comment_prefs.author.clone(); let today = app.session.today(); let by_line = app.session.stamp_by_line(&author); @@ -1170,9 +1172,10 @@ pub fn document_area(app: &mut PrintCraftApp, index: usize, ui: &mut egui::Ui) { if let QuickTool::Fill(ft) = tool && allowed { - match crate::fill_sign::page_input(ui, &resp, &xf, i, info, ft, view, signature.as_ref(), &author, today) { + match crate::fill_sign::page_input(ui, &resp, &xf, i, info, ft, view, signature.as_ref(), initials.as_ref(), &author, today) { Some(crate::fill_sign::FillAction::Edit(e)) => view.pending_edit = Some(*e), Some(crate::fill_sign::FillAction::CreateSignature) => open_signature = true, + Some(crate::fill_sign::FillAction::CreateInitials) => open_initials = true, None => {} } } @@ -1598,8 +1601,8 @@ pub fn document_area(app: &mut PrintCraftApp, index: usize, ui: &mut egui::Ui) { app.redact_prefs.mark(page, quads, &author) }); } - if open_signature { - app.signature_draft.clear(); + if open_signature || open_initials { + app.signature_draft = crate::fill_sign::SigDraft::new(open_initials, &app.comment_prefs.author); app.dialog = Some(crate::Dialog::Signature); } if open_boxes { diff --git a/crates/ui-egui/src/commands.rs b/crates/ui-egui/src/commands.rs index 18088e9b0..3eb283ed8 100644 --- a/crates/ui-egui/src/commands.rs +++ b/crates/ui-egui/src/commands.rs @@ -369,8 +369,9 @@ impl PrintCraftApp { fill if crate::fill_sign::FillTool::from_command(fill).is_some() => { let tool = crate::fill_sign::FillTool::from_command(fill).expect("checked"); self.quick_tool = crate::QuickTool::Fill(tool); - if tool == crate::fill_sign::FillTool::Signature && self.signature.is_none() { - self.signature_draft.clear(); + let initials = tool == crate::fill_sign::FillTool::Initials; + if (tool == crate::fill_sign::FillTool::Signature && self.signature.is_none()) || (initials && self.initials.is_none()) { + self.signature_draft = crate::fill_sign::SigDraft::new(initials, &self.comment_prefs.author); self.dialog = Some(Dialog::Signature); } } diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 27c912eeb..8aa3aa301 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -780,10 +780,17 @@ pub fn show(app: &mut PrintCraftApp, ctx: &egui::Context) { return; } Dialog::Signature => { - let (apply, cancel) = crate::fill_sign::signature_pad(ui, &t, &mut app.signature_draft); + let (apply, cancel) = crate::fill_sign::signature_pad(ui, &t, &mut app.signature_draft, &mut app.signature_preview); if apply { - app.signature = Some(std::mem::take(&mut app.signature_draft)); - app.quick_tool = crate::QuickTool::Fill(crate::fill_sign::FillTool::Signature); + let d = std::mem::take(&mut app.signature_draft); + let tool = if d.initials { + app.initials = Some(d.saved()); + crate::fill_sign::FillTool::Initials + } else { + app.signature = Some(d.saved()); + crate::fill_sign::FillTool::Signature + }; + app.quick_tool = crate::QuickTool::Fill(tool); app.toast = None; } close = apply || cancel; diff --git a/crates/ui-egui/src/fill_sign.rs b/crates/ui-egui/src/fill_sign.rs index 120a8ad99..8ec400e8c 100644 --- a/crates/ui-egui/src/fill_sign.rs +++ b/crates/ui-egui/src/fill_sign.rs @@ -19,10 +19,45 @@ pub enum FillTool { Line, Date, Signature, + Initials, } -pub const FILL_TOOLS: [FillTool; 7] = - [FillTool::Text, FillTool::Cross, FillTool::Check, FillTool::Dot, FillTool::Line, FillTool::Date, FillTool::Signature]; +pub const FILL_TOOLS: [FillTool; 8] = + [FillTool::Text, FillTool::Cross, FillTool::Check, FillTool::Dot, FillTool::Line, FillTool::Date, FillTool::Signature, FillTool::Initials]; + +/// A saved signature or initials: drawn strokes (normalised to the pad width, y up) or typed +/// text (drawn in the script font). +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum SavedSig { + Drawn(Vec>), + Typed(String), +} + +/// The Create signature / initials dialog. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct SigDraft { + pub strokes: Vec>, + pub text: String, + /// The Draw tab (otherwise Type). + pub drawing: bool, + /// Creating initials (otherwise the signature). + pub initials: bool, +} + +impl SigDraft { + pub fn new(initials: bool, name: &str) -> Self { + let text = if initials { name.split_whitespace().filter_map(|w| w.chars().next()).collect() } else { name.trim().to_string() }; + Self { strokes: Vec::new(), text, drawing: false, initials } + } + + fn ready(&self) -> bool { + if self.drawing { self.strokes.iter().any(|s| s.len() > 1) } else { !self.text.trim().is_empty() } + } + + pub fn saved(&self) -> SavedSig { + if self.drawing { SavedSig::Drawn(self.strokes.clone()) } else { SavedSig::Typed(self.text.trim().to_string()) } + } +} impl FillTool { pub fn command(self) -> &'static str { @@ -34,6 +69,7 @@ impl FillTool { FillTool::Line => "sign.fill.line", FillTool::Date => "sign.fill.date", FillTool::Signature => "sign.fill.signature", + FillTool::Initials => "sign.fill.initials", } } @@ -50,6 +86,7 @@ impl FillTool { FillTool::Line => "Line", FillTool::Date => "Date", FillTool::Signature => "Sign", + FillTool::Initials => "Initials", } } @@ -61,7 +98,7 @@ impl FillTool { FillTool::Dot => "circle-dot", FillTool::Line => "minus", FillTool::Date => "clock-3", - FillTool::Signature => "signature", + FillTool::Signature | FillTool::Initials => "signature", } } @@ -122,6 +159,57 @@ pub fn signature_at(page: usize, at: [f64; 2], strokes: &[Vec<[f32; 2]>], author (!strokes.is_empty()).then(|| new(page, Shape::Signature { strokes }, String::new(), author)) } +/// Place typed text in the script font with its left edge at `at`, `height` points tall. +pub fn typed_signature_at(page: usize, at: [f64; 2], text: &str, height: f64, author: &str) -> Option { + printcraft_engine::typed_signature_shape(at, text, height).map(|shape| new(page, shape, String::new(), author)) +} + +/// Place a saved signature or initials. +pub fn place(page: usize, at: [f64; 2], sig: &SavedSig, initials: bool, author: &str) -> Option { + match sig { + SavedSig::Drawn(strokes) => signature_at(page, at, strokes, author), + SavedSig::Typed(text) => typed_signature_at(page, at, text, if initials { 24.0 } else { 32.0 }, author), + } +} + +/// The text in the script font as a picture (`w`×`h` px, black on transparent), for previews. +pub(crate) fn script_preview(text: &str, w: usize, h: usize) -> egui::ColorImage { + let o = printcraft_engine::script_outline(text); + let mut img = egui::ColorImage::filled([w, h], Color32::TRANSPARENT); + let span = (o.ascent - o.descent).max(0.1); + if o.contours.is_empty() { + return img; + } + let k = ((h as f64 * 0.9) / span).min((w as f64 * 0.95) / o.width.max(0.01)); + let x0 = (w as f64 - o.width * k) / 2.0; + // Device points (y down), then an even-odd scanline fill. + let polys: Vec> = o + .contours + .iter() + .map(|c| c.iter().map(|p| (x0 + p[0] * k, h as f64 * 0.5 + (o.ascent + o.descent) / 2.0 * k - p[1] * k)).collect()) + .collect(); + for y in 0..h { + let sy = y as f64 + 0.5; + let mut xs: Vec = Vec::new(); + for poly in &polys { + for i in 0..poly.len() { + let (a, b) = (poly[i], poly[(i + 1) % poly.len()]); + if (a.1 <= sy) != (b.1 <= sy) { + xs.push(a.0 + (sy - a.1) / (b.1 - a.1) * (b.0 - a.0)); + } + } + } + xs.sort_by(|a, b| a.total_cmp(b)); + for pair in xs.chunks_exact(2) { + let (from, to) = (pair[0].round().max(0.0) as usize, (pair[1].round() as usize).min(w)); + for x in from..to { + img[(x, y)] = Color32::BLACK; + } + } + } + img +} + /// Clicks with a Fill & Sign tool on one page. Returns `true` when the click was used. #[allow(clippy::too_many_arguments)] pub(crate) fn page_input( @@ -132,7 +220,8 @@ pub(crate) fn page_input( info: &DocInfo, tool: FillTool, view: &mut DocView, - signature: Option<&Vec>>, + signature: Option<&SavedSig>, + initials: Option<&SavedSig>, author: &str, today: (i64, u32, u32), ) -> Option { @@ -155,9 +244,13 @@ pub(crate) fn page_input( Some(FillAction::Edit(Box::new(typed(page, [at[0], at[1] + TEXT_SIZE * 0.6], &format!("{m}/{d}/{y}"), author)))) } FillTool::Signature => match signature { - Some(s) => signature_at(page, at, s, author).map(|e| FillAction::Edit(Box::new(e))), + Some(s) => place(page, at, s, false, author).map(|e| FillAction::Edit(Box::new(e))), None => Some(FillAction::CreateSignature), }, + FillTool::Initials => match initials { + Some(s) => place(page, at, s, true, author).map(|e| FillAction::Edit(Box::new(e))), + None => Some(FillAction::CreateInitials), + }, mark => { let mark = mark.mark().expect("marks"); let (w, h) = if mark == FillMark::Line { (36.0, 4.0) } else { (12.0, 12.0) }; @@ -173,6 +266,8 @@ pub enum FillAction { Edit(Box), /// No signature yet: open the signature pad. CreateSignature, + /// No initials yet. + CreateInitials, } /// The in-place editor for typed text. Returns the edit once committed. @@ -220,10 +315,37 @@ pub(crate) fn type_box(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, } /// The signature pad: draw with the pointer; returns the strokes (normalised) on Apply. -pub(crate) fn signature_pad(ui: &mut egui::Ui, t: &Tokens, strokes: &mut Vec>) -> (bool, bool) { - ui.label(egui::RichText::new("Create signature").font(crate::theme::semibold(18.0))); - ui.label(egui::RichText::new("Draw your signature below.").color(t.text_muted)); +pub(crate) fn signature_pad(ui: &mut egui::Ui, t: &Tokens, d: &mut SigDraft, preview: &mut Option<(String, egui::TextureHandle)>) -> (bool, bool) { + let what = if d.initials { "initials" } else { "signature" }; + ui.label(egui::RichText::new(format!("Create {what}")).font(crate::theme::semibold(18.0))); + ui.horizontal(|ui| { + if crate::widgets::pill_button(ui, "Type", !d.drawing).clicked() { + d.drawing = false; + } + if crate::widgets::pill_button(ui, "Draw", d.drawing).clicked() { + d.drawing = true; + } + }); ui.add_space(6.0); + if !d.drawing { + let l = ui.label(egui::RichText::new(format!("Type your {what}.")).color(t.text_muted)); + ui.add(egui::TextEdit::singleline(&mut d.text).desired_width(460.0).hint_text(if d.initials { "Initials" } else { "Your name" })) + .labelled_by(l.id); + let (rect, _) = ui.allocate_exact_size(vec2(460.0, 150.0), Sense::hover()); + let painter = ui.painter_at(rect); + painter.rect_filled(rect, CornerRadius::same(6), Color32::WHITE); + painter.rect_stroke(rect, CornerRadius::same(6), Stroke::new(1.0, t.border), egui::StrokeKind::Inside); + if preview.as_ref().is_none_or(|(s, _)| *s != d.text) { + let img = script_preview(&d.text, 920, 300); + *preview = Some((d.text.clone(), ui.ctx().load_texture("typed-signature", img, egui::TextureOptions::LINEAR))); + } + if let Some((_, tex)) = preview { + painter.image(tex.id(), rect.shrink(4.0), egui::Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); + } + return pad_buttons(ui, d); + } + ui.label(egui::RichText::new(format!("Draw your {what} below.")).color(t.text_muted)); + let strokes = &mut d.strokes; let (rect, resp) = ui.allocate_exact_size(vec2(460.0, 150.0), Sense::drag()); let painter = ui.painter_at(rect); painter.rect_filled(rect, CornerRadius::same(6), Color32::WHITE); @@ -246,14 +368,19 @@ pub(crate) fn signature_pad(ui: &mut egui::Ui, t: &Tokens, strokes: &mut Vec = s.iter().map(|p| pos2(rect.left() + p[0] * rect.width(), rect.bottom() - p[1] * rect.width())).collect(); painter.add(egui::Shape::line(pts, Stroke::new(2.0, Color32::BLACK))); } + pad_buttons(ui, d) +} + +fn pad_buttons(ui: &mut egui::Ui, d: &mut SigDraft) -> (bool, bool) { ui.add_space(10.0); let (mut apply, mut cancel) = (false, false); ui.horizontal(|ui| { if ui.button("Clear").clicked() { - strokes.clear(); + d.strokes.clear(); + d.text.clear(); } ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let ready = strokes.iter().any(|s| s.len() > 1); + let ready = d.ready(); if ui.add_enabled_ui(ready, |ui| crate::widgets::pill_button(ui, "Apply", true)).inner.clicked() { apply = true; } diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index c62c6ee17..0aa5aacf5 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -349,10 +349,12 @@ pub struct PrintCraftApp { pub export_draft: export_ui::ExportDraft, /// A running export's progress. export_status: Option, - /// The saved Fill & Sign signature (strokes normalised to the pad width, y up). - pub signature: Option>>, - /// Strokes being drawn in the signature pad. - pub signature_draft: Vec>, + /// The saved Fill & Sign signature and initials (drawn or typed). + pub signature: Option, + pub initials: Option, + /// The Create signature / initials dialog, and its typed preview. + pub signature_draft: fill_sign::SigDraft, + pub(crate) signature_preview: Option<(String, egui::TextureHandle)>, /// The Comment Properties dialog's state. pub comment_props: Option, pub field_props: Option, @@ -460,7 +462,9 @@ impl PrintCraftApp { export_draft: Default::default(), export_status: None, signature: None, - signature_draft: Vec::new(), + initials: None, + signature_draft: Default::default(), + signature_preview: None, comment_props: None, field_props: None, redact_prefs: RedactPrefs::default(), @@ -751,7 +755,10 @@ impl PrintCraftApp { serde_json::json!({ "recent": self.recent, "theme": self.theme, - "signature": self.signature, + // Drawn signatures keep their original form (older settings read the same). + "signature": match &self.signature { Some(fill_sign::SavedSig::Drawn(s)) => Some(s), _ => None }, + "signature_text": match &self.signature { Some(fill_sign::SavedSig::Typed(t)) => Some(t), _ => None }, + "initials": self.initials, // Keychain identities are read from macOS each time. "digital_ids": self.digital_ids.iter().filter(|e| !e.path.starts_with("keychain:")).collect::>(), "trusted": trusted, @@ -775,7 +782,13 @@ impl PrintCraftApp { if let Ok(s) = serde_json::from_value::>>(v["signature"].clone()) && s.iter().all(|st| st.iter().all(|p| p.iter().all(|x| x.is_finite()))) { - self.signature = Some(s); + self.signature = Some(fill_sign::SavedSig::Drawn(s)); + } + if let Some(t) = v["signature_text"].as_str().filter(|t| !t.trim().is_empty()) { + self.signature = Some(fill_sign::SavedSig::Typed(t.to_string())); + } + if let Ok(i) = serde_json::from_value::(v["initials"].clone()) { + self.initials = Some(i); } if let Ok(ids) = serde_json::from_value::>(v["digital_ids"].clone()) { self.digital_ids = ids; diff --git a/crates/ui-egui/tests/fill_sign.rs b/crates/ui-egui/tests/fill_sign.rs index f848466e8..19613bb6b 100644 --- a/crates/ui-egui/tests/fill_sign.rs +++ b/crates/ui-egui/tests/fill_sign.rs @@ -74,6 +74,8 @@ fn signing_draws_a_signature_once_and_places_it() { assert!(h.state_mut().execute("sign.fill.signature")); h.run_steps(2); assert_eq!(h.state().dialog, Some(Dialog::Signature), "no signature yet: the pad opens"); + h.get_all_by_label("Draw").last().unwrap().click(); + h.run_steps(2); let pad = h.get_by_label("Draw your signature below.").rect(); let start = pos2(pad.left() + 40.0, pad.bottom() + 70.0); h.hover_at(start); @@ -98,3 +100,34 @@ fn signing_draws_a_signature_once_and_places_it() { again.restore(&saved); assert!(again.signature.is_some()); } + +#[test] +fn typed_signatures_and_initials() { + let mut h = harness(); + h.state_mut().comment_prefs.author = "Grace Hopper".into(); + assert!(h.state_mut().execute("sign.fill.signature")); + h.run_steps(2); + // Type is the default, with the author's name filled in. + h.get_by_label("Type your signature."); + assert_eq!(h.state().signature_draft.text, "Grace Hopper"); + h.get_by_label("Apply").click(); + h.run_steps(3); + assert_eq!(h.state().signature, Some(printcraft_ui_egui::fill_sign::SavedSig::Typed("Grace Hopper".into()))); + click(&mut h, 60.0, 100.0); + assert!(items(&h).iter().any(|(t, _)| t == "Stamp"), "typed signatures are filled outlines"); + // Initials: their own pad (GH), then placed. + assert!(h.state_mut().execute("sign.fill.initials")); + h.run_steps(2); + h.get_by_label("Create initials"); + assert_eq!(h.state().signature_draft.text, "GH"); + h.get_by_label("Apply").click(); + h.run_steps(3); + click(&mut h, 60.0, 160.0); + assert_eq!(items(&h).iter().filter(|(t, _)| t == "Stamp").count(), 2); + // Both are remembered. + let saved = h.state().persist(); + let mut again = PrintCraftApp::new(); + again.restore(&saved); + assert_eq!(again.signature, h.state().signature); + assert_eq!(again.initials, Some(printcraft_ui_egui::fill_sign::SavedSig::Typed("GH".into()))); +} diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 731945e82..9087c783e 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -5176,10 +5176,10 @@ area = "F" tier = "P0" milestone = "M5" status = "partial" -commands = ["sign.fill.signature"] -tools = [] -evidence = ["crates/ui-egui/tests/fill_sign.rs::signing_draws_a_signature_once_and_places_it"] -notes = "drawn signature, saved in settings; typed/image signatures and initials not yet" +commands = ["sign.fill.signature", "sign.fill.initials"] +tools = ["fill_sign_add"] +evidence = ["crates/ui-egui/tests/fill_sign.rs::typed_signatures_and_initials", "crates/ui-egui/tests/fill_sign.rs::signing_draws_a_signature_once_and_places_it", "crates/fonts/src/script.rs::a_name_becomes_outlines", "crates/automation/tests/automation.rs::fill_and_sign_through_tools"] +notes = "signature and initials, typed (Dancing Script outlines as filled paths, no font embedded) or drawn, saved in settings; image signatures not yet" [[feature]] id = "form.fill-sign-flatten"