Add watermark to image before submit form

This blog will show you how to add watermark text to image and replace the input’s filelist with new one.

Our code is written in Stimulus Controller without using any external library

import { Controller } from "@hotwired/stimulus"

class Watermarker {
  constructor (width = 1400, height = 800) {
    this.width = width
    this.height = height
  }

  getImage (file) {
    return new Promise((resolve, _) => {
      const img = document.createElement('img')
      img.addEventListener("load", () => {
        URL.revokeObjectURL(file)
        resolve(img)
      })
      img.src = URL.createObjectURL(file)
    })
  }

  render (file, text, color = "black") {
    return new Promise(async (resolve, _) => {
      const canvas = document.createElement('canvas')
    
      const image = await this.getImage(file)
      // Can get image size (width, height) from image object

      canvas.width = this.width
      canvas.height = this.height
  
      const ctx = canvas.getContext('2d')
      ctx.drawImage(image, 0, 0, this.width, this.height)
      ctx.fillStyle = color
      ctx.textBaseline = "middle"
      ctx.font = "bold 60px serif"
      ctx.translate(canvas.width / 2, canvas.height / 2)
      ctx.rotate(-Math.PI / 4)
      ctx.textAlign = "center"
      ctx.fillText(text, 0, 0)
      ctx.restore()

      canvas.toBlob((blob) => {
        resolve(blob)
      })
    })
  }
}

export default class extends Controller {

  static targets = [ "previewer" ]

  connect () {
    this.text = this.data.get("text") || "Apply for identification"
    this.color = this.data.get("color") || "red"
    this.override = this.data.has("override") ? this.data.get("override") === "true" : false
  }
  
  async transform (event) {
    const file = event.target.files[0]
    const newBlob = await (new Watermarker()).render(file, this.text, this.color)

    if (this.override) {
      const newFile = new File([newBlob], file.name, { type: file.type })
      const dataTransfer = new DataTransfer()
      dataTransfer.items.add(newFile)
      event.target.files = dataTransfer.files
    }
    
    this.previewerTarget.src = URL.createObjectURL(newBlob)
  }
}

Orginial Image File

Original Image

Watermarked Image File

Watermarked Image