Skip to content

20 fix watermark#20

Merged
simonabler merged 11 commits intomasterfrom
20-fix-watermark
Mar 11, 2026
Merged

20 fix watermark#20
simonabler merged 11 commits intomasterfrom
20-fix-watermark

Conversation

@simonabler
Copy link
Copy Markdown
Owner

No description provided.

Simon Abler added 11 commits March 11, 2026 15:21
watermark.service.ts was sending String(1) instead of String(opts.opacity)
for the 'opacity' FormData field. The opacity slider in the UI had no effect —
every request went to the backend with opacity=1 regardless of the setting.

Changed to String(opts.opacity ?? 0.5) to pass the actual value.
buildTextTileSvg and buildLogoTileSvg used `dto.rotate ?? -30` as fallback
for the pattern rotation. When a user set rotate=0 explicitly, the tile
would still be rendered at -30 degrees — inconsistent with single-placement
mode which correctly used `dto.rotate ?? 0`.

Changed both tile builders to use `dto.rotate ?? 0` so the default is
no rotation, matching user expectations and the single-placement behaviour.
Backend (watermark.controller.ts):
  FileFieldsInterceptor now has limits: { fileSize: 25 * 1024 * 1024 }.
  Large uploads are rejected by Multer before Sharp ever touches them,
  avoiding memory exhaustion from large in-memory buffers.

Frontend (watermark-uploader.component.ts/.html):
  setMainFile() and setLogoFile() now check file.size > MAX_FILE_BYTES
  (25 MB) before accepting the file. Shows an inline alert under the
  respective dropzone. formatBytes() helper formats the error message.
  Prevents the confusing Multer 413/500 response from reaching the user.
…imited

Sharp image processing is CPU-intensive. Without @TierRateLimit() the
/watermark/apply endpoint was completely unguarded against request floods.

watermark.controller.ts: @TierRateLimit() added to POST apply.
watermark.module.ts:
  - ApiKeyModule imported (required for ApiKeyGuard resolution)
  - MulterModule.register limit raised from 15 MB to 25 MB to align with
    the FileFieldsInterceptor limits added in the previous commit.
The previous implementation resolved the default logo path relative to
process.cwd() which is the repo root in development but the dist output
dir in production Docker containers — making the path wrong in both cases.

Replaced with __dirname-relative paths:
  __dirname/assets/watermark/default-logo.png  (dist layout)
  __dirname/../assets/watermark/default-logo.png  (fallback)

__dirname always points to the directory containing the compiled JS file,
which is consistent across local and container environments.
When the backend returns a 400/413/500, Angular's responseType:'blob' still
delivers a Blob to the next() handler instead of routing to error(). The
component would then call URL.createObjectURL() on a JSON payload and display
a broken image with no error message.

watermark.service.ts now wraps the HTTP call in an Observable that inspects
the response blob's content-type. If it is not image/* the blob is read as
text, parsed as JSON, and re-thrown as a proper Error so the component's
catchError / error handler can display the message to the user.
…inates

The drag overlay was positioned in CSS pixels (relative to the rendered
preview div) but the x,y values were sent directly to the backend as
image pixel coordinates. A 4000x3000 image rendered at 800x300 CSS px
would produce a 5x offset between where the overlay appeared and where
the watermark was actually placed.

Changes:
  watermark-uploader.component.ts:
    - Added @ViewChild previewImg to access the rendered <img> element
    - naturalWidth/naturalHeight tracked via onImageLoad() handler
    - scaleX / scaleY computed as naturalSize / renderedSize
    - startDrag: converts stored image-px coords → CSS px for drag offset
    - onPointerMove: converts CSS-px drag result → image-px for form/backend

  watermark-uploader.component.html:
    - #previewImg template ref + (load)=onImageLoad added to <img>
    - Overlay left/top now divided by scaleX/scaleY so the draggable dot
      stays visually in sync with where the backend will place the mark
…English

All German strings in watermark.controller.ts and apply-watermark.dto.ts
replaced with English equivalents to be consistent with the rest of the API
documentation. Affects Swagger UI display and BadRequestException messages.
Adding @TierRateLimit() brought ApiKeyGuard into the module, which requires
TypeORM / DataSource. The test module was importing WatermarkModule directly
and had no database context, causing the suite to fail.

Rewrote the spec to wire only what it needs:
  - MulterModule.register (memory storage)
  - WatermarkController + WatermarkService directly
  - ApiKeyGuard overridden with { canActivate: () => true }

286/286 tests pass.
…osite error

Sharp requires that composite overlays are never larger than the base image
and that top/left coordinates keep the overlay fully within bounds.

Two failure modes fixed:

1. Logo scale=1 (frontend default) or a logo larger than the input image:
   targetWidth was uncapped, producing an overlay wider than the base image.
   Fix: cap targetWidth to min(calculated, imageWidth) before scaling.

2. After anchor/position calculation top or left could be negative or push
   the overlay past the image edge (e.g. margin=0, bottom-right on a logo
   that fills the full width).
   Fix: clamp top  to [0, imageH - wmH]
         and left to [0, imageW - wmW] after resolving position.

scaleLogoToWidth now accepts an optional maxHeight and passes it to
Sharp.resize() with withoutEnlargement:true so the logo is never upscaled.

Also translated the two remaining German BadRequestException messages.
@simonabler simonabler merged commit 9296805 into master Mar 11, 2026
1 of 2 checks passed
@simonabler simonabler deleted the 20-fix-watermark branch March 15, 2026 08:29
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.

1 participant