Signature / Insert

A compositor for adding one or more overlays — images, text, auto-populated technical cards, callouts with connector lines — on top of the active document, transforming them interactively, and baking the result back into the image.

The dialog is non-modal and designed around a scene / preview model: your overlays live as free-floating objects on a canvas on top of the base image, you drag, rotate, scale, and snap them until you like what you see, then Affix Inserts flattens everything into the document. Nothing is destructive until you affix.

1. Dialog layout

Two columns separated by a draggable splitter whose position is remembered across sessions:

Most sections start collapsed; the ones expanded by default are the ones you reach for first (Load, Transform, Inset Callout, Apply).

2. Loading inserts

Three ways to add a pixmap overlay, in the Load / Add section:

Transparency hint. The yellow hint at the top of the Load section says it plainly: use Load from File if you want to keep PNG alpha. Loading from an open View captures RGB only.

3. Transforming overlays interactively

Every overlay — pixmap, text, or technical card — is a free-floating scene item with the same interaction model.

3.1 The blue handle

A small blue dot sits on the top-right of each item's bounding box. Click and drag it to scale and rotate simultaneously, both anchored at the item's centre:

Letting go leaves the item at its new scale and rotation. See § 11.1 for the exact math.

3.2 Click, drag, and select

3.3 Transform group

The Transform section gives three controls that act on whichever items are selected:

4. Text inserts

The Text section (collapsed by default) adds freely-styled text overlays with optional outlines.

Changing any of these controls applies instantly to the current selection. Font, size, bold, italic, fill colour, outline colour, and outline width all persist to QSettings and come back the way you left them next session.

5. Technical Card

The Technical Card is a special overlay that auto-populates with FITS header / document metadata. It's the right tool for the little “OBJECT / DATE / EXPOSURE / FILTER” corner block you see on finished astrophotos.

5.1 Enabling the card

Toggle Enable Tech Card to create (or remove) the card. Enabling drops the card at the top-left corner, populated with your default field set. You can drag, scale, rotate, snap, and style it just like any other overlay.

5.2 Field catalogue

The Fields combo lists every available field: the actual FITS header keys from the current document (OBJECT, DATE-OBS, EXPTIME, FILTER, INSTRUME, TELESCOP, FOCALLEN, XPIXSZ, GAIN, OFFSET, BAYERPAT, … all of them, straight from the header SASpro loaded with the image), plus four computed convenience fields:

5.3 Building the card

The default set is OBJECT, DATE-OBS, EXPTIME, FILTER, GAIN, OFFSET, INSTRUME, TELESCOP, FOCALLEN, XPIXSZ, BAYERPAT, Size, Bit Depth, Mono — whatever subset of those actually exists in the current document's header. Customise with:

Build / Update re-renders the card with the current field list and style; most style changes also re-render live. Reset restores the whole configuration to defaults.

5.4 Card style

Text inside the card uses the same Font / Fill / Outline / Outline width controls as regular text inserts (§ 4). Every setting here persists to QSettings, including the ordered field list.

6. Bounding Box Style

Pixmap inserts get a little rectangle drawn around them on screen so you can see where they are. The Bounding Box Style section controls this overlay guide (which is not baked into the image unless Draw Bounding Box is on at affix time):

Turn Draw off when you're ready to affix if you want the final image without any outline around the insert.

7. Inset Callout

The Inset Callout section attaches a source region to a pixmap insert, drawing a rectangle around that region on the main image and connecting it to the insert with two straight lines. Common use: drop a 3× crop of the galaxy's core in the corner and show visually where it came from.

7.1 Setting a source region

  1. Select a pixmap insert on the canvas.
  2. Click Set Source Region. The cursor becomes a crosshair; the status label at the bottom of the section prompts you to drag.
  3. Drag a rectangle on the main image to define the source. A dashed cyan preview follows the pointer.
  4. Release — the dashed preview is replaced by the callout: a solid rectangle in the chosen callout colour around the source, and two connector lines to the insert's bounding box.
  5. Right-click during the drag to cancel.

Clear Source Region drops the callout from the selected insert without removing the insert itself.

7.2 Connector modes

The Connector mode combo picks which corners the two lines connect:

7.3 Callout style

Connector lines auto-track: moving, scaling, or rotating the insert redraws the lines so they always start at the source and end at the insert's current corners. A 16-ms refresh timer keeps this live.

8. Create Insert from Current

A fast way to make a zoomed inset of a feature in your own image. Click Create Insert from Current… — a dialog asks:

After the choice, drag on the preview to define the source rectangle. The region is cropped from the base pixmap, upscaled 3× with smooth interpolation, and added as a new insert at the centre of the scene — drag it, snap it, scale it, position it just like any other insert. Right-click during the drag cancels the operation.

9. Send to Position

Snap the selected items to any of nine standard anchor positions, relative to the base image, with configurable margins.

These same nine positions are reachable by right-clicking any overlay on the canvas. The nine-position + margin model also drives the headless replay, so a signature placed in the top-right once with margins (30, 30) can be re-applied to any other image via the Replay system with the same arguments.

10. Zoom and preview

The Zoom section has –, +, and Fit buttons; the preview also responds to Ctrl + wheel for cursor-anchored zoom (0.10× to 10×) and left-click-drag anywhere empty for pan.

11. Apply / Clear

The Apply / Clear section has three actions:

Non-destructive until you affix. Loading, moving, styling, deleting individual overlays, closing and reopening the dialog — none of that changes the document. Only Affix Inserts commits.

Overlays can extend past the base image. If an insert hangs off the edge of your base image, the affixed output grows to include it. The output rectangle is the union of every committed item's bounding box in scene coordinates, so a signature placed at (−20, H+30) will push the final image a few pixels outside the original edges. If you don't want that, keep overlays inside the base image bounds, or use Send to Position with positive margins.

12. Persistence

Nearly every control value is persisted to QSettings under the signature_insert/ namespace and comes back next session:

Changing any of these also triggers a save, so you never need to remember to confirm.

13. Technical details

Signature / Insert is built on QGraphicsScene / QGraphicsView. The base image is the scene's bottom item (z = 0) and every overlay is a free-floating scene item above it (z = 1). Interaction model, item types, and the bake step are described below.

13.1 TransformHandle: scale + rotate from one drag

Each movable overlay carries a child TransformHandle object (small blue circle) parked on the item's top-right corner by a 16-ms sync timer. On mouse press the handle records the vector from the item's centre to its own position:

d \;=\; \sqrt{(x_h - x_c)^2 \,+\, (y_h - y_c)^2}
\theta \;=\; \mathrm{atan2}(y_h - y_c,\;x_h - x_c)

On mouse move, both are recomputed from the new handle position, and the item's scale and rotation are updated to:

\mathrm{scale}_{\mathrm{new}} \;=\; \mathrm{scale}_{\mathrm{start}} \,\cdot\, \frac{d_{\mathrm{new}}}{d_{\mathrm{start}}}
\Delta\theta \;=\; \theta_{\mathrm{new}} \;-\; \theta_{\mathrm{start}}

Scale is floored at 0.05 so the item never collapses to zero size. Each item's transform origin is set to its own centre, so both scale and rotation apply around the centre (the natural pivot for signature placement).

13.2 OutlinedTextItem: dual-pass painting

Text overlays extend QGraphicsTextItem with an outline pass. When outline width is zero, painting uses the fast QGraphicsTextItem code path (plain text rendering). When outline width is > 0, painting switches to a vector approach: build a QPainterPath from the text's glyph outlines line by line, stroke it with the outline colour and width, fill with the fill colour. The stroke uses RoundCap + RoundJoin so sharp inner corners don't produce visible spikes. This path is rebuilt on every paint, which is cheap for the character counts typically on screen; the pay-off is a crisp outline at any scale and rotation.

13.3 TechCardItem: composite container

The tech card is a composite QGraphicsItem that owns three children: a background rectangle, a border rectangle, and the OutlinedTextItem for the field block. The card's padding determines the offset between the text and the two rectangles; its bounding rect is the union of all three. Rebuilding (changing padding, text, or style) calls prepareGeometryChange(), repositions the text at (padding, padding), resizes bg and border to the new content extent, and refreshes the transform origin to the new centre. The item itself paints nothing — its children do.

13.4 Position anchoring with margins

The 9 snap positions compute the item's new top-left in base-image local coordinates:

x_{\mathrm{left}} \;=\; m_x
x_{\mathrm{right}} \;=\; W_{\mathrm{base}} \;-\; W_{\mathrm{item}} \;-\; m_x
x_{\mathrm{centre}} \;=\; \frac{W_{\mathrm{base}} \;-\; W_{\mathrm{item}}}{2}

and the same three rules on the y axis, giving nine (x, y) combinations. The result is mapped to scene coordinates through the base item's transform, so the snap stays correct even if the view is zoomed or the base is positioned somewhere other than (0, 0).

13.5 Callout connector geometry

Callouts connect two corners of the source rectangle to two corners of the insert's bounding rectangle. The direction mode selects which corners:

All callout items use cosmetic pens so their stroke width is in pixels, not scene units — zooming the preview doesn't change how thick the lines look on screen.

13.6 Baking (Affix Inserts)

Flattening the scene to the output image works in these steps:

  1. Deselect every item so no selection highlights paint through. Collapse every text item's internal selection (the character-range highlight that persists after editing) and set text-interaction to None, so the final image has no editor UI leaking into it.
  2. Enumerate the items to render: base image, pixmap inserts, text overlays, tech card, visible callout rectangles and lines, and bounding boxes only if those are enabled.
  3. Compute the union of their scene-space bounding rectangles. This is the output rectangle, which can be larger than the base image (see § 11's warning).
  4. Create an ARGB32 QImage sized to the union, fill with transparency, and call scene.render(painter, target, source). Any item not in the render list is temporarily hidden during the render.
  5. Restore visibility, selection, text-interaction, and focus to every item.
  6. Drop the alpha channel, clip to [0, 1], hand the resulting RGB float32 image back to the document via set_image() with step name “Signature / Insert”.

13.7 Headless replay

For scripted / replay use, apply_signature_preset_to_doc(doc, preset) performs a single-image overlay with the preset keys:

The headless path recomputes the overlay's visual bounding box after scale and rotation (because a rotated rectangle's axis-aligned bbox is larger than the original), snaps that visual bbox to the chosen anchor with the margins applied, draws through a QImage canvas with full alpha handling, and returns an RGB float32 image in [0, 1]. This is the one replayable through SASpro's last-command / preset system; the full interactive composition (multiple inserts, callouts, text, tech card) is not auto-replayable — use it for a quick signature stamp.

Credits. Signature / Insert is entirely SASpro's own design. The interaction model (single-handle simultaneous scale + rotate, drag-to-define regions, right-click 9-position snap, cosmetic pens so stroke widths stay constant across zoom) is built on Qt's QGraphics framework (QGraphicsScene / View / Item). The outlined text renderer uses QPainterPath stroke-and-fill. The tech card header-dict builder accepts astropy-style Header objects via duck typing (no astropy import), as well as plain Python dicts and lists of (key, value) pairs, so it works with whatever header representation DocManager is holding for the current document.