tool_image_rotation
Rotates and proportionally crops an image component's derived files across every quality tier — the original master is always preserved — and hosts an in-browser AI background-removal pipeline.
What it does / why & when to use it
A component_image stores one preserved master (the original quality) plus a set of generated derivatives (default, 404, thumb, 1.5MB, …), each a real file on disk indexed by the component's files_info block. tool_image_rotation lets an operator straighten or reframe an image without re-uploading it: it opens the image in an interactive editor, applies a rotation angle and an optional crop rectangle, and re-renders every non-original tier server-side with ImageMagick. The master is never touched, so the transform is always redoable from the pristine original.
The tool bundles a second, unrelated capability on the same panel: background removal, which runs the briaai/RMBG-1.4 neural network entirely in the browser (WebGPU, WASM fallback), then uploads the cut-out PNG and re-derives the quality set through tool_upload.
Concrete heritage scenario: a photo archivist uploads a scanned print that came off the scanner sideways and with a black border. They open Image rotation on the image component, drag the angle slider until the horizon is level (with the alignment axes and "Expand" mode preventing corner clipping), draw a crop box to drop the border, and press the apply button. Every web/thumbnail quality is regenerated straight and cropped; the archived master stays exactly as scanned.
Use it when: a derived image needs straightening, reframing, or its subject isolated on a transparent background. It is image-only — the server refuses any component whose model is not component_image. To manage the wider quality set (build, delete, conform AV headers, sync), use tool_media_versions; to get a file into the component in the first place, use the upload flow / tool_upload / tool_import_files.
How it works (server + client)
Server (tools/tool_image_rotation/server/index.ts). One API action, apply_rotation, declaratively gated permission: 'record', minLevel: 2 (write + per-record project scope). The handler resolves the media tool context (resolveMediaToolContext, src/core/media/tool_support.ts), refuses unless spec.model === 'component_image', reads the tiers to touch from the stored files_info (falling back to a live disk scan via getFilesInfoCore when the record has none), and delegates to the rotation core applyRotationCore (src/core/media/tools/rotation.ts). After the transform it re-scans disk and writes the refreshed files_info back to the record with persistScannedFilesInfo (rotation changes tier dimensions, so the cached metadata must be renewed). It returns { result, msg, errors, rotated, cropped, files_info }.
The rotation core enforces the Original law: it iterates the entries, skips spec.originalQuality and any tier whose file is absent, and rewrites each derivative through a temp-file + rename. The rotate pass is skipped when degrees is 0 or NaN. The crop pass runs only when a cropArea with positive width and height is supplied; each tier's box is scaled from the default-quality reference dimensions, and rotate errors are collected per tier rather than aborting the whole run.
Client (tools/tool_image_rotation/js/). tool_image_rotation.js is the instance (extends the tool_common lifecycle); it pins self.main_element to the ddo_map entry flagged role: 'main_element' — the component_image being edited. render_tool_image_rotation.js builds the editor: a live CSS-transform preview, an angle slider and numeric input synchronised bidirectionally (range -360…360, step 0.01), a background colour picker, a "Transparent" (alpha) toggle, an "Expand" toggle that grows the container to the rotated bounding box so corners are not clipped, alignment-axis guides, and the crop overlay. The apply button gathers the parameters and calls self.apply_rotation(), which confirms (confirm(get_label.sure)), builds the dd_tools_api / tool_request RQO, dispatches through data_manager.request, and on success force-reloads the cache-busted preview. render_tool_image_crop.js is the drag-to-select crop overlay (rubber-band draw, eight compass-point resize handles, move) that maintains render_tool_image_crop.crop_area — read directly by apply_rotation.
Background removal. The "Remove background" button drives automatic_background_removal(), which spawns core/tools_common/js/processors/remove_background/remove_background.js as a module Worker, runs briaai/RMBG-1.4 (device 'webgpu'), converts the model output to a PNG blob, uploads it with service_upload, and calls the exported process_uploaded_file from tools/tool_upload/js/tool_upload.js to re-derive the quality set into the 'modified' quality. ua.check_transformers_webgpu() gates compatibility with a confirm() warning when WebGPU is absent. This path is client-only — it never calls tool_image_rotation's server action.
crop_area units
The rotation core (src/core/media/tools/rotation.ts) documents cropArea as a proportional box (fractions 0..1) of the default-quality reference, scaled per tier. The crop overlay (render_tool_image_crop.js::update_crop_area) currently emits the box in natural (integer pixel) coordinates. This discrepancy is called out as unverified — treat the server contract (fractional) as authoritative and confirm the client mapping before relying on the crop pass.
Actions & options
apply_rotation is the only remotely callable action. permission: 'record', minLevel: 2 runs before the handler. There is no backgroundRunnable — the rotation runs inline. Required options are tipo (the image component tipo), section_tipo, section_id; the transform parameters are read from the same options object.
| Action | Gate | Options it reads | Purpose |
|---|---|---|---|
apply_rotation |
record, level 2 |
tipo, section_tipo, section_id (req.); rotation_degrees (number; 0 skips the rotate pass); rotation_mode ('expanded' grows the canvas, else 'default'); background_color (hex, e.g. #ffffff; default #ffffff); crop_area ({ x, y, width, height } or null) |
Rotate + optionally crop every non-original tier; refresh and persist files_info. |
Response: { result, msg, errors, rotated, cropped, files_info } — result is true when errors is empty; rotated / cropped list the touched absolute paths.
alpha and the client
The client sends an alpha flag (transparent background) alongside the rotation options, but the server handler maps only rotation_degrees, rotation_mode, background_color and crop_area into the core call. Transparency at render time is governed by the tier's own extension/format (alpha-capable formats such as PNG/AVIF), not by a distinct server option.
How it is registered & surfaced
tools/tool_image_rotation/register.json is a column-keyed dump (a seeded matrix-row snapshot, not a hand-authored file); importTools() passes it through as-is (see register.json reference). Essentials it carries:
dd1326name =tool_image_rotation;dd799label = "Image rotation" (per project language);dd612description = "Accurately manages image rotation".dd1327version =2.0.0;dd1328minimum Dédalo version =6.7.0;dd1644developer = "Dédalo team".dd1330affected_models → the image model. The tool attaches tocomponent_imagecomponents, not to sections or areas — matching the server's image-only guard.- No
dd1335properties block with anopen_askey, so the tool opens in the default modal. dd1372labels supply the localized UI strings across project languages:rotation,bk_colour,transparent,expand,degrees,remove_background,backgroun_removal_completed,processing_image,setting_up,procesing,cpu_device. (backgroun_removal_completedis spelled without the seconddin the registration — reproduced verbatim.)
Surfacing (in getElementTools, src/core/tools/registry.ts): because affected_models names the image model, the Image rotation button appears on component_image when it is rendered, subject to the dd1331 show_in_inspector / dd1332 show_in_component flags. The presence of the tool on the component is visible in src/core/components/component_image/samples/context.json.
Examples
Client tool_request built by create_source(self, 'apply_rotation') and sent through dd_tools_api. Rotate every derived tier by 90° with a white background, no crop:
const rqo = {
dd_api : 'dd_tools_api',
action : 'tool_request',
source : create_source(self, 'apply_rotation'), // → tool_image_rotation::apply_rotation
options : {
tipo : self.main_element.tipo, // e.g. 'rsc29' (the image component)
section_tipo : self.main_element.section_tipo, // e.g. 'rsc170'
section_id : self.main_element.section_id, // e.g. 1
rotation_degrees : 90,
rotation_mode : 'expanded',
background_color : '#ffffff',
alpha : false,
crop_area : null
}
}
const response = await data_manager.request({ body: rqo })
// response → { result:true, msg:'ok', errors:[], rotated:[...], cropped:[], files_info:[...] }
Rotate a small angle and crop (crop box read from the overlay's render_tool_image_crop.crop_area):
const options = {
rotation_degrees : output.value, // e.g. '64.8'
background_color : color_picker.value, // '#ffffff'
alpha : alpha_checkbox.checked,
rotation_mode : expanded_checkbox.checked ? 'expanded' : 'default',
crop_area : render_tool_image_crop.crop_area // { x, y, width, height } or null
}
const ok = await self.apply_rotation(options) // confirms, then dispatches apply_rotation
Related
- tool_media_versions — the broader per-quality manager (build, delete, conform headers, sync); it also exposes a per-quality rotate as a
specific_action, whereastool_image_rotationis the full interactive rotate + crop editor forcomponent_image. - tool_upload — the post-upload ingest action this tool calls to re-derive the quality set after background removal.
- tool_posterframe — the AV/3D still-frame tool; the other media editor hosting a component preview.
- Media pipeline — the end-to-end media lifecycle (master → derivatives → protection → diffusion) this tool operates within.
- Creating new tools · Server contract · Security — the tool model,
apiActions, permission gates and lifecycle this page builds on. - Source:
tools/tool_image_rotation/server/index.ts,tools/tool_image_rotation/register.json,tools/tool_image_rotation/js/{tool_image_rotation,render_tool_image_rotation,render_tool_image_crop}.js,tools/tool_image_rotation/css/tool_image_rotation.less. The rotation core issrc/core/media/tools/rotation.ts(ImageMagick adapter undersrc/core/media/engine/).