How to let a user crop and scale an image?

One of our use cases consists of the user being able to create a visually appealing pdf. Therefore they are able to upload images as attachments which will be displayed in fixed sized and fixed positioned frames in the preview of the pdf. Now the user should be able to crop and scale the image they uploaded.

If anyone else did implement something like that how did you do that?
Is there a widget element that I can use for that purpose?
How should cropping and scaling data be stored in our documents?

Hi @andreas-fresh-mesa,

I don’t have hands-on experience with image cropping/scaling in an A12 context and I’m not aware of any project that has implemented this. That said, I have some thoughts on how you could approach it.

Have you already started implementing something on your own? If so, it would be great if you could share what you have so far — that would help the community give more targeted feedback.

Here is a condensed approach of how I would start implementing that:

1. Frontend — Cropping UI via a custom widget

There is no built-in A12 widget for image cropping. You’d need to integrate a JavaScript cropping library into a custom widget.

For Form Engine and Overview Engine they support customizing how attachment previews are rendered:

  • Form Engine: Use the WidgetMap to replace the default DefaultFileUpload widget for your attachment control with a custom React component that wraps the cropper library. You can also customize the SelectorMap (specifically attachmentThumbnail) to render a cropped preview instead of the raw image.
  • Overview Engine: Similarly provides a custom SelectorMap with attachmentThumbnail — so you can render cropped thumbnails in list views as well.

Further reading:

2. Backend — Print Engine and other consumers

Every backend endpoint that renders the image visually (most notably the Print Engine) needs to be aware of the crop metadata and apply it before rendering. The Print Engine’s AttachmentProvider gives you the raw ByteArrayInputStream — you’d need to wrap or extend it to apply crop/scale transformations before passing the result to the print job.

The same applies to any custom download or export endpoint that should serve the cropped version.

3. Storing crop metadata

I’d recommend storing the crop/scale parameters as a sibling group next to the attachment group in your Document Model — something like an image_crop_settings group with fields for cropX, cropY, cropWidth, cropHeight, and scale (all numbers).

Adding custom fields directly inside the attachment group is technically possible since it’s just a regular group, but the standard attachment fields (attachment_id, content, mime_type, etc.) are managed by the AttachmentHandler/AttachmentLoader pipeline and I would be cautious about mixing custom fields in there — it could lead to unexpected behavior when attachments are replaced or deleted.

A sibling group keeps things separated and future proof: the attachment group handles the binary, the crop-settings group holds the visual framing. Both live on the same document and are always saved together.

Note: If you have multiple image attachments per document that each need independent crop settings, consider making the crop-settings group repeatable or pairing each attachment with its own dedicated crop group.

Thanks for that sophisticated question I am interested in if you’ve already explored any of these directions or have specific constraints that i had not considered right now.

And :slight_smile: like stated often, feel free to create a dedicated requirement ticket for the A12 platform if you think this is a feature that should be added (go to https://support.mgm-tp.com/)

Hi @mschmahl,

Thank you for your detailed suggestions. We’ve now successfully implemented image cropping and wanted to share a clean summary of our approach.

1. Data Modeling — Storing Crop Metadata

We store crop parameters as a sibling group next to the attachment, rather than embedding them inside it.

We create a reusable CropImageData-DM that contains:

  • Image (standard A12 attachment fields)

  • Crop with three fields:

    • x → horizontal offset

    • y → vertical offset

    • zoom → scale factor

This model is included wherever crop-enabled images are needed.

2. Frontend — Custom Content Engine Image Module (react-easy-crop)

Instead of extending the Form Engine (WidgetMap / SelectorMap), we implemented everything inside the A12 Content Engine.

2a. Custom Image Module

We replaced: DefaultElementModules.Image in the ViewerElementLibrary.

Key idea:

  • Reuse everything from the default module

  • Override only the renderer

Supported states:

  • View mode → cropped preview

  • Edit mode → preview + actions

  • Crop mode → interactive cropper

2b. Crop UI — react-easy-crop Integration

We used react-easy-crop for the crop/pan/zoom UX.

Key decisions:

  • Free panning
    Allow users to move beyond image bounds

  • Dynamic aspect ratio

  • Zoom control

    • Custom numeric input (%) + +/- buttons or use mouse to zoom in/out.
  • Lightweight & Dependency-Friendly

2c. Rendering the Cropped Result

We intentionally avoided:

  • :cross_mark: Canvas

  • :cross_mark: Server-side image processing

Instead, we use pure CSS transformations:

  • Replicate object-fit: cover behavior

  • Apply:

    • zoom → scale

    • x, y → offsets

  • Render via:

    • position: absolute

    • computed width, height, left, top

3. Backend — Data Migration

We don’t store cropped images — only the original image + crop metadata (x, y, zoom). No server-side cropping, CSS handles everything at render time. Since the document structure changed (moving attachments into CropImageData-DM), we implemented a migration.

Hope this helps anyone implementing image cropping in an A12 project.