Skip to content

DSS engine: add background image layering, text auto-scaling, and tolerant field placement#433

Open
sam2kb wants to merge 3 commits into
intoolswetrust:masterfrom
sam2kb:pr-dss-visual
Open

DSS engine: add background image layering, text auto-scaling, and tolerant field placement#433
sam2kb wants to merge 3 commits into
intoolswetrust:masterfrom
sam2kb:pr-dss-visual

Conversation

@sam2kb

@sam2kb sam2kb commented Jun 27, 2026

Copy link
Copy Markdown

Add background image layering, text auto-scaling & tolerant placement to DSS engine

What
Background image support. The DSS engine currently only allows a single image - either a signature graphic or a background. This PR adds proper layering: you can now set a background image (with scale control) that renders behind the signature graphic behind the text, all in one signature block.

Text that fits any rectangle. DSS defaults to a fixed font size - text gets cut off on small signature boxes. This switches to auto-scaling mode (FILL_BOX_AND_LINEBREAK), where the font shrinks to fit whatever rectangle you draw, wrapping lines naturally. The user's font size preference still acts as an upper cap (no giant text on huge boxes).

Transparent text background. DSS draws a solid white rectangle behind signature text, which covers any background image underneath. This sets it to transparent so the background image shows through.

Friendlier annotation overlap. If your visible signature rectangle overlaps an existing PDF annotation (e.g. an Adobe stamp), DSS throws an error and refuses to sign. This changes it to a warning - signing proceeds, and the signature appears on top.

Why
These are the features our users needed to fully replace the old OpenPdf engine with DSS:

Background images are essential for professional stamps, seals, and letterhead
Auto-scaling text is critical for workflows with varying signature box sizes
Overlap tolerance is needed when signing over documents that already have annotations

How
Four new classes in engines/dss/.../pdfbox/ plus changes to DssSigningEngine.java to wire them in. All behaviour is opt-in via the existing GUI controls - nothing changes for users who don't use these features.

…erant field placement

- JSignPdfSignatureImageParameters: extends DSS SignatureImageParameters
  with backgroundImage, backgroundScale, and overrides isEmpty() so
  background-only signatures are not silently skipped.

- JSignPdfOverlaySignatureDrawer: custom PDFBox drawer that renders
  background image → foreground graphic → text in layers, capped at
  the user's preferred font size when FILL_BOX_AND_LINEBREAK is used.

- JSignPdfPdfObjFactory / JSignPdfSignatureDrawerFactory: wire the
  custom drawer into PdfBoxSignatureService.

- DssSigningEngine changes:
  * Use JSignPdfPdfObjFactory with LogOnStatusAlert (warn instead of
    throw) when the signature field overlaps existing annotations.
  * Use JSignPdfSignatureImageParameters for background+foreground
    image support (upstream limits to a single image).
  * Set TextWrapping.FILL_BOX_AND_LINEBREAK so text auto-scales to
    fit the signature rectangle.
  * Set text background to transparent so the background image shows
    through the signature text.
@kwart

kwart commented Jul 5, 2026

Copy link
Copy Markdown
Member

Thanks for this — background layering + autoscaling text are genuinely useful additions to the DSS engine. Since the DSS engine hasn't shipped yet (targeting 3.1), I've focused on intrinsic correctness, licensing, and structure. A few things I'd like to see addressed before this lands as the shipped engine:

Please fix before merge

  1. Background image is ignored by getExpectedColorSpaceName() (JSignPdfOverlaySignatureDrawer). It only inspects parameters.getImage() (the foreground graphic). For a background-only signature, it falls through to text color → DEVICEGRAY, and DSS's checkColorSpace() can then inject a DEVICEGRAY output intent into a document that actually contains an RGB/CMYK background image. That's wrong output and a PDF/A inconsistency. The background image needs to participate in color-space determination.

  2. Licensing header. JSignPdfOverlaySignatureDrawer is a near-verbatim fork of DSS's LGPL-2.1 NativePdfBoxVisibleSignatureDrawer, with the copyright/LGPL notice stripped. Please restore the DSS header (or clearly attribute the origin) — this is the moment to get it right before it ships in a release artifact.

  3. Dropped image rotation. The custom setImageScaledToBox() doesn't apply the per-image rotation transform that DSS's setImage() had. If the engine advertises field rotation, a rotated field will render an unrotated graphic.

  4. isEmpty() override is effectively dead. DSS's AbstractPdfBoxSignatureDrawer.init() validates via assertSignatureParametersAreValid(), which checks getImage()==null && text.isEmpty() directly — it never consults your overridden isEmpty(). So a true background-only signature (no foreground image, no text) still throws "Neither image nor text parameters are defined!". It only works today because configureVisibleSignature() always sets L2 text. Either make background-only genuinely reachable or drop the override.

Structural concern

  1. This is a 415-line fork of DSS internals with no tests. It couples to non-public classes (PdfBoxDSSFontMetrics, PdfBoxFontMapper, SignatureFieldDimensionAndPosition.setTextSize()), which can shift on any DSS bump. See the drawer-selection suggestion below, which quarantines this risk, plus please add at least a smoke test (sign with background + graphic + text; assert a valid signature and no spurious output intent).

Suggestion: select the drawer automatically instead of always replacing it

Right now JSignPdfSignatureDrawerFactory returns the custom drawer for every signature, so the fragile fork is in the hot path even for plain text/graphic signatures that DSS's stock drawer already handles well. Instead, let the input pick the drawer — the factory already receives the parameters, which is DSS's designed extension point:

public class JSignPdfSignatureDrawerFactory implements PdfBoxSignatureDrawerFactory {
    @Override
    public PdfBoxSignatureDrawer getSignatureDrawer(SignatureImageParameters p) {
        // Only use the custom layering drawer when a background image is actually configured
        if (p instanceof JSignPdfSignatureImageParameters jp && jp.getBackgroundImage() != null) {
            return new JSignPdfOverlaySignatureDrawer();
        }
        // Otherwise fall back to DSS's stock native drawer
        return new NativePdfBoxVisibleSignatureDrawer();
    }
}

Why this is worth doing:

  • Quarantines the fork — the fragile custom drawer only runs when someone sets a background image. A DSS upgrade that breaks it can't break the default signing path.
  • Clean PDF/A path by default — the stock drawer has no transparency and no color-space gap (Translation in Greek #1 above), so signatures without a background image stay compliant automatically.
  • No new config/GUI knobs — the behavior follows the inputs the user already supplies, rather than a mode toggle users can't reason about.

Separately, the overlap relaxation (LogOnStatusAlert) is currently applied globally in DssSigningEngine.sign(). That one is a real policy choice with no input to infer it from, so I'd make it an explicit opt-in defaulting to DSS's strict throw — safe by default, escape hatch for those who knowingly sign over existing annotations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants