Adds a "wrap" visual treatment to signed signature and initials fields
in the result PDF, the live signing UI, and the post-sign view, so
recipients see a consistent bracketed layout matching industry-standard
signed-document conventions (DocuSign / Adobe Sign etc.).
Motivation
----------
Built for a residential real-estate signing workflow where
clients and title companies are accustomed to the bracketed signature
visual format. The current DocuSeal output is a centered signature image
with optional plain-text caption beside or below it; signed initials are
a bare image with no caption. This change closes the visual gap as an
opt-in style — only renders when with_signature_id is enabled (existing
config flag, default false). Sharing in case useful upstream; happy to
iterate on the design or split into smaller commits.
Change
------
- HexaPDF rendering rewrite in lib/submissions/generate_result_attachments.rb:
- Left-side amber bracket via path drawing, height matched to image
- Centered signature image
- Helvetica-Bold header ("Digitally signed by:" / "Initials:")
- Optional caption (name + timestamp) and truncated document ID
- Adaptive layout: drops ID line, then caption line, before crushing
the image when field is small
- Page-derived font sizing so signatures and initials match
- Mirrored layout in the live signing UI (app/javascript/submission_form/area.vue)
and the post-sign view (app/views/submissions/_value.html.erb) so the
signer's review and the completed-submission page match the PDF
- Typed initials font alias in lib/submitters/generate_font_image.rb
switched from Go Noto Bold to Dancing Script — matches the typed
signature font, fixing the visual inconsistency where typed signatures
rendered cursive but typed initials rendered sans-serif
Trade-offs
----------
- Helvetica vs Go Noto for header/caption: Helvetica is one of the 14
standard PDF fonts, keeps the credibility text crisp. Non-Latin
scripts in the bracket text fall back to Helvetica's coverage.
The signature image itself still renders correctly using Go Noto.
- No data-model change. Pure rendering layer.
- Default behavior unchanged for existing users (with_signature_id
is still false by default in upstream).
Out of scope
------------
- Strikethrough field unhide (separate PR)
- Default-flag flips for with_signature_id, with_disclosure (opinionated)