Watermarking Images with WordPress with WP_Image_Editor

I really like the WP_Image_Editor class. I wanted to use it for some custom functionality I’m making, but I was wondering – Watermarking images – is there a way that I could use the objects created by WP_Image_Editor to merge an image with a watermark? I cannot figure out any obvious way.

Related posts

2 comments

  1. If you really want to use these classes the only way would be to extend both existing implementations Wp_Image_Editor_Imagick and Wp_Image_Editor_GD.

    Here’s an approach for the Wp_Image_Editor_GD:

    namespace WPSE98156;
    
    use
        Wp_Image_Editor_Gd,
        Wp_Error;
    
    class WatermarkImageEditor extends Wp_Image_Editor_Gd {
    
        /*
         * @param resource $stamp (A GD image resource)
         *
         * @return bool|WP_Error
         */
        public function stamp_watermark( $stamp ) {
    
            /**
             * The resource of the image to edit is stored in
             * $this->image, after $this->load() was called
             */
            $loaded = $this->load();
            if ( is_wp_error( $loaded ) )
                return $loaded;
    
            // Set the margins for the stamp and get the height/width of the stamp image
            $marge_right = 10;
            $marge_bottom = 10;
            $sx = imagesx( $stamp );
            $sy = imagesy( $stamp );
    
            // Copy the stamp image onto our photo using the margin offsets and the photo
            // width to calculate positioning of the stamp.
            imagecopy(
                $this->image,
                $stamp,
                imagesx( $this->image ) - $sx - $marge_right,
                imagesy( $this->image ) - $sy - $marge_bottom,
                0,
                0,
                imagesx( $stamp ),
                imagesy( $stamp )
            );
        }
    
        /**
         * @param array $args
         *
         * @return bool
         */
        public static function test( $args = [] ) {
    
            /**
             * Maybe implement your own test here, whether the environment
             * is able to deal with your implementation of the
             * stamp_watermark() method
             */
            return parent::test( $args );
        }
    
        /**
         * @param string $mime_type
         *
         * @return bool
         */
        public static function supports_mime_type( $mime_type ) {
    
            /**
             * Todo: Check here if the implementation of the method stamp_watermark()
             * can deal with the mime-types image/png, image/jpeg and image/gif
             */
            return parent::supports_mime_type( $mime_type );
        }
    }
    

    The implementation follows this example on php.net.

    Now that you have your implementation you must add the new class to the stack of possible image editors using the filter wp_image_editors:

    namespace WPSE98156;
    
    add_filter( 'wp_image_editors', function( $editors ) {
    
        if ( ! is_array( $editors ) )
            return $editors; //someone broke the filtered value
    
        array_unshift( $editors, WatermarkImageEditor::class );
    
        return $editors;
    } );
    

    Now it’s likely possible to get an instance of your custom editor when calling wp_get_image_editor():

    $editor = wp_get_image_editor( '/path/to/image.jpeg' );
    if ( ! is_wp_error( $editor ) && is_callable( [ $editor, 'stamp_watermark' ] ) && ! is_wp_error( $loaded ) ) {
    
        $stamp = imagecreatefrompng( '/path/to/watermark.png' );
        $success = $editor->stamp_watermark( $stamp );
        if ( ! is_wp_error( $success ) )
            $editor->save();
    }
    

    You could also simply create the instance explicitly. Every other client, using wp_get_image_editor() won’t be aware of the method stamp_watermark() anyway.

    Some important notes:

    • The code is not tested. It’s meant to provide a first approach on how to extend the Wp_Image_Editor_Gd class.
    • The examples show syntax that requires at least PHP 5.5
    • All tasks about loading required files are not shown. Use proper auto loading.
    • I’m not into the details of the GD API. So I’m not sure about supported mime-types, you should test this and implement the methods test() and supportet_mime_types() according to this.
  2. I have had a lot of problems implementing David code. I had errors like “WP_IMAGE_EDITOR_GD” not found, “WatermarkImageEditor” not found…
    And when I got it to work, it didn’t work with alpha channel png, so I lost a lot of hours making it work with them. So I will explain how I have it.

    Put all this code in a php file, mine is called watermark.php.

    <?php
    class WatermarkImageEditor extends WP_Image_Editor_GD {
        public function stamp_watermark( $stampPath, $marginH=0, $marginV=0 ) {
            $loaded = $this->load();
            if ( is_wp_error( $loaded ) ) return $loaded;
    
            $stamp=imagecreatefrompng( $stampPath );
            if(is_wp_error($stamp)){ return $stamp; }
            imagealphablending($stamp, true);
    
            $sx = imagesx( $stamp );
            $sy = imagesy( $stamp );
            imagealphablending($this->image, true);
            imagecopy(
                $this->image, $stamp,$marginH,$this->size['height']-$sy-$marginV,0,0,$sx, $sy
            );  
        }
    
        public static function test( $args = [] ) { return parent::test( $args ); }
    
        public static function supports_mime_type( $mime_type ) { return parent::supports_mime_type( $mime_type ); }
    }
    

    Now, we need to register the filter. I am using it in my own plugin, so I have this code on my main plugin file, but you can also put it on other place like functions.php. Note that I am using require_once to load watermark.php, so watermark.php has to be in the same folder.

    add_filter( 'wp_image_editors', function( $editors ) {
        require_once __DIR__. '/watermark.php';
        if ( ! is_array( $editors ) )
            return $editors; //someone broke the filtered value
        array_unshift( $editors, "WatermarkImageEditor");
        return $editors;
    } );
    

    And the last step, call to stamp_watermark(). On this example, I am loading an image from disk, resizing it, putting the watermark, and saving it. Note that stamp_watermark() receives on the first parameter, the path or url of the watermark, the other 2 parameters are the optional margin.

    $editor= wp_get_image_editor($imagePath);
    $editor->resize(1920, 1080, TRUE);
    if(is_callable([$editor,'stamp_watermark'])){
      $success = $editor->stamp_watermark( ABSPATH.'wp-content/uploads/watermark-full.png', 20, 20 );       
      if(!is_wp_error($success)){ $editor->save($imagePath); }
    }
    

Comments are closed.