Friends of OpenJDK Today

Dynamic watermarking on the JVM

July 09, 2024

Author(s)

  • Avatar photo
    Nicolas Frankel

    Nicolas is a developer advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). ... Learn more

Displaying images on your website makes for an interesting problem: on one side, you want to make them publicly available; on the other, you want to protect them against undue use. The age-long method to achieve it is watermarking:

A digital watermark is a kind of marker covertly embedded in a noise-tolerant signal such as audio, video or image data. It is typically used to identify ownership of the copyright of such signal. "Watermarking" is the process of hiding digital information in a carrier signal; the hidden information should, but does not need to, contain a relation to the carrier signal. Digital watermarks may be used to verify the authenticity or integrity of the carrier signal or to show the identity of its owners. It is prominently used for tracing copyright infringements and for banknote authentication.

-- Digital watermarking

The watermark can be visible to act as a deterrent to people stealing the image; alternatively, you can use it to prove its origin after it has been stolen.

However, if there are too many images on a site, it can be a burden to watermark them beforehand. It can be much simpler to watermark them dynamically. I searched for an existing JVM library dedicated to watermarking but surprisingly found nothing. We can achieve that in a Jakarata EE-based web app with the Java 2D API and a simple Filter.

The Java 2D API has been part of the JDK since 1.0, and it shows.

It translates into the following code:

private fun watermark(imageFilename: String): BufferedImage? {
    val watermark = ImageIO.read(ClassPathResource("/static/$imageFilename").inputStream) ?: return null //1
    val watermarker = ImageIO.read(ClassPathResource("/static/apache-apisix.png").inputStream) //2
    watermark.createGraphics().apply {                         //3
        drawImage(watermarker, 20, 20, 300, 300, null)         //4
        dispose()                                              //5
    }
    return watermark
}
  1. Get the original image
  2. Get the watermarking image
  3. Get the canvas of the original image
  4. Draw the watermark. I was too lazy to make it partially transparent
  5. Release system resources associated with this object

Other stacks may have dedicated libraries, such as photon-rs for Rust and WebAssembly. With this in place, we can move to the web part. As mentioned above, we need a Filter.

class WatermarkFilter : Filter {

    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        val req = request as HttpServletRequest
        val imageFilename = req.servletPath.split("/").last()  //1
        val watermarked = watermark(imageFilename)             //2
        response.outputStream.use {
            ImageIO.write(watermarked, "jpeg", it)             //3
        }
    }
}
  1. Get the image filename
  2. Watermark the image
  3. Write the image in the response output stream

I explained how to watermark images on a Java stack in this post. I did the watermark manually because I didn't find any existing library. Next week, I'll show a no-code approach based on infrastructure components.

To go further:


Originally published at A Java Geek on June 30th, 2024

Promoted Content

Step up your coding with the Continuous Feedback Udemy Course: Additional coupons are available

What do you know about the code changes that were just introduced into the codebase? When will you notice if something goes wrong?

Get Started Here!

Topics:

Related Articles

View All

Author(s)

  • Avatar photo
    Nicolas Frankel

    Nicolas is a developer advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). ... Learn more

Comments (0)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard