WordPress’te Yüklenen Görsellere Otomatik Watermark Ekleme

WordPress tabanlı bir e-ticaret sitesi veya fotoğraf portfolyo sitesi yönetiyorsanız, yüklenen görsellerin izinsiz kullanımı ciddi bir sorun haline gelebilir. Özellikle WooCommerce mağazalarında ürün fotoğraflarınızın rakipler tarafından kopyalanması hem telif hakkı hem de marka kimliği açısından büyük kayıplara yol açar. İşte tam bu noktada otomatik watermark (filigran) sistemi devreye girer. Bu yazıda WordPress functions.php dosyasına ekleyeceğiniz fonksiyonlarla, yüklenen her görsele otomatik olarak watermark nasıl eklenir, detaylıca inceleyeceğiz.

Watermark Mantığı ve WordPress Media Sistemi

WordPress bir görsel yüklendiğinde birkaç farklı boyut üretir. Bunlar thumbnail, medium, large ve orijinal boyuttur. Watermark sistemi kurmanın en mantıklı yolu, wp_handle_upload veya wp_generate_attachment_metadata hook’larını kullanmaktır.

Temel akış şu şekildedir:

  • Kullanıcı bir görsel yükler
  • WordPress görseli işler ve boyutları oluşturur
  • Hook tetiklenir, watermark fonksiyonu devreye girer
  • PHP’nin GD veya Imagick kütüphanesi watermark’ı görselin üzerine işler
  • Watermark’lı görsel orijinalin yerine kaydedilir

Başlamadan önce sunucunuzda GD kütüphanesinin aktif olduğunu kontrol edin:

php -r "echo function_exists('imagecreatefromjpeg') ? 'GD aktif' : 'GD yok';"

Imagick kontrolü için:

php -r "echo class_exists('Imagick') ? 'Imagick aktif' : 'Imagick yok';"

Eğer GD yoksa sunucunuza yüklemeniz gerekir:

# Ubuntu/Debian
sudo apt-get install php-gd
sudo systemctl restart apache2

# CentOS/RHEL
sudo yum install php-gd
sudo systemctl restart httpd

Temel Watermark Fonksiyonu

İlk adım olarak en sade haliyle çalışan bir watermark fonksiyonu yazalım. Bu fonksiyon PNG formatındaki bir watermark görselini yüklenen her JPEG ve PNG dosyasının üzerine yerleştirir.

<?php
// functions.php dosyasina ekleyin

function wm_add_watermark_to_upload( $metadata, $attachment_id ) {
    
    // Watermark gorsel yolu
    $watermark_path = get_template_directory() . '/assets/images/watermark.png';
    
    // Watermark dosyasi yoksa islemi atla
    if ( ! file_exists( $watermark_path ) ) {
        return $metadata;
    }
    
    // Yuklenen gorselin tam yolunu al
    $upload_dir = wp_upload_dir();
    $file_path  = $upload_dir['basedir'] . '/' . $metadata['file'];
    
    // Dosya uzantisini kontrol et
    $file_ext = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
    
    if ( ! in_array( $file_ext, array( 'jpg', 'jpeg', 'png' ) ) ) {
        return $metadata;
    }
    
    // Ana gorsele watermark ekle
    wm_apply_watermark( $file_path, $watermark_path, $file_ext );
    
    return $metadata;
}

add_filter( 'wp_generate_attachment_metadata', 'wm_add_watermark_to_upload', 10, 2 );

Bu temel yapı bize bir çerçeve sunar. Asıl watermark uygulama işini yapan wm_apply_watermark fonksiyonunu şimdi yazalım:

function wm_apply_watermark( $source_path, $watermark_path, $ext ) {
    
    // Kaynak gorseli yukle
    switch ( $ext ) {
        case 'jpg':
        case 'jpeg':
            $source_image = imagecreatefromjpeg( $source_path );
            break;
        case 'png':
            $source_image = imagecreatefrompng( $source_path );
            break;
        default:
            return false;
    }
    
    if ( ! $source_image ) {
        return false;
    }
    
    // Watermark gorselini yukle
    $watermark_image = imagecreatefrompng( $watermark_path );
    
    if ( ! $watermark_image ) {
        imagedestroy( $source_image );
        return false;
    }
    
    // Boyutlari hesapla
    $source_width    = imagesx( $source_image );
    $source_height   = imagesy( $source_image );
    $watermark_width = imagesx( $watermark_image );
    $watermark_height = imagesy( $watermark_image );
    
    // Watermark'i sag alt koseye yerleştir (20px kenar boslugu)
    $dest_x = $source_width - $watermark_width - 20;
    $dest_y = $source_height - $watermark_height - 20;
    
    // Alpha blending aktif et
    imagealphablending( $source_image, true );
    
    // Watermark'i uygula
    imagecopy(
        $source_image,
        $watermark_image,
        $dest_x,
        $dest_y,
        0,
        0,
        $watermark_width,
        $watermark_height
    );
    
    // Gorseli kaydet
    switch ( $ext ) {
        case 'jpg':
        case 'jpeg':
            imagejpeg( $source_image, $source_path, 90 );
            break;
        case 'png':
            imagepng( $source_image, $source_path );
            break;
    }
    
    // Bellegi temizle
    imagedestroy( $source_image );
    imagedestroy( $watermark_image );
    
    return true;
}

Dinamik Boyutlandırma ile Akıllı Watermark

Gerçek dünya senaryosunda bir sorunla karşılaşırsınız: küçük görsellere büyük watermark çirkin görünür, büyük görsellere küçük watermark ise neredeyse görünmez olur. Bu sorunu çözmek için watermark boyutunu kaynak görsele oransal olarak ayarlayan gelişmiş bir versiyon yazalım.

function wm_apply_watermark_scaled( $source_path, $watermark_path, $ext, $opacity = 70 ) {
    
    switch ( $ext ) {
        case 'jpg':
        case 'jpeg':
            $source_image = imagecreatefromjpeg( $source_path );
            break;
        case 'png':
            $source_image = imagecreatefrompng( $source_path );
            break;
        default:
            return false;
    }
    
    if ( ! $source_image ) {
        return false;
    }
    
    $watermark_image = imagecreatefrompng( $watermark_path );
    
    if ( ! $watermark_image ) {
        imagedestroy( $source_image );
        return false;
    }
    
    $source_width     = imagesx( $source_image );
    $source_height    = imagesy( $source_image );
    $watermark_width  = imagesx( $watermark_image );
    $watermark_height = imagesy( $watermark_image );
    
    // Watermark genisligi kaynak gorselin %30'u olsun
    $target_wm_width  = intval( $source_width * 0.30 );
    $scale_ratio      = $target_wm_width / $watermark_width;
    $target_wm_height = intval( $watermark_height * $scale_ratio );
    
    // Minimum boyut kontrolu (cok kucuk olmasin)
    if ( $target_wm_width < 50 ) {
        imagedestroy( $source_image );
        imagedestroy( $watermark_image );
        return false;
    }
    
    // Watermark'i yeniden boyutlandir
    $scaled_watermark = imagecreatetruecolor( $target_wm_width, $target_wm_height );
    imagealphablending( $scaled_watermark, false );
    imagesavealpha( $scaled_watermark, true );
    
    $transparent = imagecolorallocatealpha( $scaled_watermark, 0, 0, 0, 127 );
    imagefilledrectangle( $scaled_watermark, 0, 0, $target_wm_width, $target_wm_height, $transparent );
    
    imagecopyresampled(
        $scaled_watermark,
        $watermark_image,
        0, 0, 0, 0,
        $target_wm_width,
        $target_wm_height,
        $watermark_width,
        $watermark_height
    );
    
    // Opakligi ayarla (GD ile yaklasik opaklik efekti)
    $dest_x = $source_width - $target_wm_width - 30;
    $dest_y = $source_height - $target_wm_height - 30;
    
    imagealphablending( $source_image, true );
    
    imagecopymerge(
        $source_image,
        $scaled_watermark,
        $dest_x,
        $dest_y,
        0, 0,
        $target_wm_width,
        $target_wm_height,
        $opacity
    );
    
    // Kaydet
    switch ( $ext ) {
        case 'jpg':
        case 'jpeg':
            imagejpeg( $source_image, $source_path, 92 );
            break;
        case 'png':
            imagepng( $source_image, $source_path );
            break;
    }
    
    imagedestroy( $source_image );
    imagedestroy( $watermark_image );
    imagedestroy( $scaled_watermark );
    
    return true;
}

WooCommerce Ürün Görselleri için Özel Kural

WooCommerce kullanan bir sitede her türlü görsele watermark eklemek istemeyebilirsiniz. Mesela blog yazısı kapak fotoğrafları, logo ya da ikon dosyaları watermark gerektirmez. Sadece WooCommerce ürün görselleri ve galeri fotoğraflarını hedefleyen filtrelenmiş bir sistem kuralım.

function wm_watermark_woocommerce_images( $metadata, $attachment_id ) {
    
    // WooCommerce aktif mi kontrol et
    if ( ! class_exists( 'WooCommerce' ) ) {
        return $metadata;
    }
    
    $watermark_path = get_template_directory() . '/assets/images/watermark.png';
    
    if ( ! file_exists( $watermark_path ) ) {
        return $metadata;
    }
    
    $upload_dir = wp_upload_dir();
    $file_path  = $upload_dir['basedir'] . '/' . $metadata['file'];
    $file_ext   = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
    
    if ( ! in_array( $file_ext, array( 'jpg', 'jpeg', 'png' ) ) ) {
        return $metadata;
    }
    
    // Bu gorselin hangi post'a baglandigini bul
    $post_parent = wp_get_post_parent_id( $attachment_id );
    
    if ( ! $post_parent ) {
        // Parent yoksa, post_meta'dan kontrol et
        // Upload sirasinda henuz baglanmamis olabilir
        // Bu durumda session veya transient ile isaretleyebilirsiniz
        // Simdilik devam ediyoruz
    }
    
    // Eger parent bir WooCommerce urunu ise watermark ekle
    if ( $post_parent && get_post_type( $post_parent ) === 'product' ) {
        wm_apply_watermark_scaled( $file_path, $watermark_path, $file_ext, 75 );
        
        // Alt boyutlara da watermark ekle
        if ( isset( $metadata['sizes'] ) ) {
            foreach ( $metadata['sizes'] as $size => $size_data ) {
                $size_path = dirname( $file_path ) . '/' . $size_data['file'];
                if ( file_exists( $size_path ) ) {
                    $size_ext = strtolower( pathinfo( $size_path, PATHINFO_EXTENSION ) );
                    wm_apply_watermark_scaled( $size_path, $watermark_path, $size_ext, 75 );
                }
            }
        }
    }
    
    return $metadata;
}

add_filter( 'wp_generate_attachment_metadata', 'wm_watermark_woocommerce_images', 10, 2 );

Metin Tabanlı Watermark

Bazen PNG watermark dosyası hazırlamak zahmetli olabilir ya da dinamik olarak site adınızı watermark olarak eklemek isteyebilirsiniz. GD kütüphanesiyle metin tabanlı watermark da mümkündür.

function wm_add_text_watermark( $source_path, $ext, $text = null ) {
    
    if ( ! $text ) {
        $text = get_bloginfo( 'name' );
    }
    
    switch ( $ext ) {
        case 'jpg':
        case 'jpeg':
            $source_image = imagecreatefromjpeg( $source_path );
            break;
        case 'png':
            $source_image = imagecreatefrompng( $source_path );
            break;
        default:
            return false;
    }
    
    if ( ! $source_image ) {
        return false;
    }
    
    $source_width  = imagesx( $source_image );
    $source_height = imagesy( $source_image );
    
    // Font dosyasi yolu (sunucunuzda mevcut bir TTF font kullanin)
    $font_path = get_template_directory() . '/assets/fonts/OpenSans-Bold.ttf';
    
    // Font boyutunu gorsele gore ayarla
    $font_size = intval( $source_width / 20 );
    $font_size = max( 12, min( $font_size, 72 ) );
    
    // Metin boyutlarini hesapla
    $text_box    = imagettfbbox( $font_size, 0, $font_path, $text );
    $text_width  = abs( $text_box[4] - $text_box[0] );
    $text_height = abs( $text_box[5] - $text_box[1] );
    
    // Metin pozisyonu (sag alt kose)
    $text_x = $source_width - $text_width - 20;
    $text_y = $source_height - $text_height - 10;
    
    // Golge rengi (siyah, yari saydam)
    $shadow_color = imagecolorallocatealpha( $source_image, 0, 0, 0, 60 );
    
    // Metin rengi (beyaz, yari saydam)
    $text_color = imagecolorallocatealpha( $source_image, 255, 255, 255, 30 );
    
    imagealphablending( $source_image, true );
    
    // Once golge yaz (1px offset)
    imagettftext( $source_image, $font_size, 0, $text_x + 2, $text_y + 2, $shadow_color, $font_path, $text );
    
    // Sonra asil metni yaz
    imagettftext( $source_image, $font_size, 0, $text_x, $text_y, $text_color, $font_path, $text );
    
    switch ( $ext ) {
        case 'jpg':
        case 'jpeg':
            imagejpeg( $source_image, $source_path, 92 );
            break;
        case 'png':
            imagepng( $source_image, $source_path );
            break;
    }
    
    imagedestroy( $source_image );
    
    return true;
}

Mevcut Görselleri Toplu İşleme

Yeni yüklenen görseller için sistemi kurdunuz ama ya önceden yüklenmiş binlerce ürün görseli var? Bunları tek tek işlemek yerine WP-CLI ile toplu işlem yapabilirsiniz.

# Tum eklerin meta verilerini yeniden olustur (bu hook'u yeniden tetikler)
wp media regenerate --yes

# Sadece belirli bir post'un eklerini yeniden olustur
wp media regenerate 123 456 789

# Belirli bir tarihten sonra yuklenen gorselleri bul
wp post list --post_type=attachment --after="2024-01-01" --format=ids | xargs wp media regenerate

Alternatif olarak doğrudan PHP’den toplu watermark uygulayan bir fonksiyon yazabilirsiniz. Bunu bir WP-CLI komutu olarak da kaydedebilirsiniz:

function wm_bulk_watermark_existing_images() {
    
    // Sadece admin veya WP-CLI'dan calissin
    if ( ! is_admin() && ! defined( 'WP_CLI' ) ) {
        return;
    }
    
    $watermark_path = get_template_directory() . '/assets/images/watermark.png';
    
    if ( ! file_exists( $watermark_path ) ) {
        error_log( 'Watermark dosyasi bulunamadi: ' . $watermark_path );
        return;
    }
    
    $args = array(
        'post_type'      => 'attachment',
        'post_mime_type' => array( 'image/jpeg', 'image/png' ),
        'post_status'    => 'inherit',
        'posts_per_page' => 50,         // Hafiza icin batch boyutu
        'paged'          => 1,
    );
    
    $upload_dir = wp_upload_dir();
    $page       = 1;
    $processed  = 0;
    
    do {
        $args['paged'] = $page;
        $query         = new WP_Query( $args );
        
        if ( ! $query->have_posts() ) {
            break;
        }
        
        foreach ( $query->posts as $attachment ) {
            
            $file_path = get_attached_file( $attachment->ID );
            
            if ( ! $file_path || ! file_exists( $file_path ) ) {
                continue;
            }
            
            $file_ext = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
            
            // Zaten watermark uygulanmis mi kontrol et
            $already_watermarked = get_post_meta( $attachment->ID, '_wm_watermark_applied', true );
            
            if ( $already_watermarked ) {
                continue;
            }
            
            $result = wm_apply_watermark_scaled( $file_path, $watermark_path, $file_ext, 70 );
            
            if ( $result ) {
                // Meta olarak isaretleyelim, tekrar islenmemesi icin
                update_post_meta( $attachment->ID, '_wm_watermark_applied', current_time( 'mysql' ) );
                $processed++;
                
                if ( defined( 'WP_CLI' ) ) {
                    WP_CLI::log( "Islendi: {$attachment->ID} - " . basename( $file_path ) );
                }
            }
        }
        
        wp_reset_postdata();
        $page++;
        
        // Her batch arasinda biraz bekle, sunucuyu asma
        if ( ! defined( 'WP_CLI' ) ) {
            sleep( 1 );
        }
        
    } while ( $query->max_num_pages >= $page );
    
    if ( defined( 'WP_CLI' ) ) {
        WP_CLI::success( "Toplam {$processed} gorsel islendi." );
    }
    
    return $processed;
}

Watermark’ı Belirli Kategorilerden veya Kullanıcılardan Muaf Tutma

Gerçek dünya senaryosunda bazı istisnalar her zaman gereklidir. Örneğin editör veya admin kullanıcıları tarafından yüklenen görseller watermark almıyor olabilir, ya da belirli kategoriler için watermark devre dışı bırakılabilir.

function wm_should_apply_watermark( $attachment_id ) {
    
    // Mevcut kullanicinin rolunu kontrol et
    $user = wp_get_current_user();
    
    // Admin ve Editor rolleri watermark'tan muaf
    $exempt_roles = array( 'administrator', 'editor' );
    
    foreach ( $exempt_roles as $role ) {
        if ( in_array( $role, (array) $user->roles ) ) {
            return false;
        }
    }
    
    // Belirli bir post tipine bagli mi kontrol et
    $post_parent = wp_get_post_parent_id( $attachment_id );
    
    if ( $post_parent ) {
        $post_type = get_post_type( $post_parent );
        
        // Sadece product ve portfolio post tiplerine watermark uygula
        $watermark_post_types = array( 'product', 'portfolio' );
        
        if ( ! in_array( $post_type, $watermark_post_types ) ) {
            return false;
        }
    }
    
    // Cok kucuk gorseller icin watermark ekleme (ikon boyutlari)
    $metadata = wp_get_attachment_metadata( $attachment_id );
    
    if ( isset( $metadata['width'] ) && $metadata['width'] < 300 ) {
        return false;
    }
    
    if ( isset( $metadata['height'] ) && $metadata['height'] < 300 ) {
        return false;
    }
    
    return true;
}

// Ana watermark fonksiyonunu bu kontrolle zenginlestirin
function wm_conditional_watermark( $metadata, $attachment_id ) {
    
    if ( ! wm_should_apply_watermark( $attachment_id ) ) {
        return $metadata;
    }
    
    $watermark_path = get_template_directory() . '/assets/images/watermark.png';
    
    if ( ! file_exists( $watermark_path ) ) {
        return $metadata;
    }
    
    $upload_dir = wp_upload_dir();
    $file_path  = $upload_dir['basedir'] . '/' . $metadata['file'];
    $file_ext   = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
    
    if ( ! in_array( $file_ext, array( 'jpg', 'jpeg', 'png' ) ) ) {
        return $metadata;
    }
    
    wm_apply_watermark_scaled( $file_path, $watermark_path, $file_ext, 70 );
    
    // Meta olarak kaydet
    update_post_meta( $attachment_id, '_wm_watermark_applied', current_time( 'mysql' ) );
    
    return $metadata;
}

add_filter( 'wp_generate_attachment_metadata', 'wm_conditional_watermark', 10, 2 );

Hata Ayıklama ve Log Sistemi

Prodüksiyon ortamında watermark sisteminin düzgün çalışıp çalışmadığını izlemek için basit bir loglama mekanizması ekleyelim:

function wm_log( $message, $type = 'info' ) {
    
    if ( ! defined( 'WM_DEBUG' ) || ! WM_DEBUG ) {
        return;
    }
    
    $log_file = WP_CONTENT_DIR . '/watermark-debug.log';
    $timestamp = current_time( 'Y-m-d H:i:s' );
    $log_entry = "[{$timestamp}] [{$type}] {$message}" . PHP_EOL;
    
    file_put_contents( $log_file, $log_entry, FILE_APPEND | LOCK_EX );
}

// wp-config.php dosyasina ekleyin (debug modu icin):
// define( 'WM_DEBUG', true );

Log dosyasını izlemek için sunucuda:

# Canli log takibi
tail -f /var/www/html/wp-content/watermark-debug.log

# Son 50 satiri gor
tail -n 50 /var/www/html/wp-content/watermark-debug.log

# Hatalari filtrele
grep '[error]' /var/www/html/wp-content/watermark-debug.log

Watermark PNG Dosyası Hazırlama İpuçları

Kod ne kadar iyi olursa olsun, watermark görseli kaliteli değilse sonuç çirkin görünür. Komut satırından ImageMagick ile hızlıca watermark oluşturabilirsiniz:

# Seffaf arka planli metin watermark olustur
convert -size 400x100 xc:transparent 
  -font /usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf 
  -pointsize 48 
  -fill 'rgba(255,255,255,0.7)' 
  -gravity center 
  -annotate 0 'SITENIZIN ADI' 
  watermark.png

# Mevcut logonuzu seffaflastirin (%70 opaklik)
convert logo.png -alpha set 
  -channel Alpha 
  -evaluate multiply 0.7 
  watermark.png

# Watermark boyutunu kontrol et
identify watermark.png

Performans Optimizasyonu

Yüksek trafikli bir WooCommerce sitesinde görsel yükleme işlemleri yavaşlayabilir. Birkaç pratik önlem:

  • PHP memory_limit: Büyük görseller için en az 256MB olmalı, wp-config.php dosyasına define('WP_MEMORY_LIMIT', '256M'); ekleyin
  • max_execution_time: Toplu işlemler için set_time_limit(300); ekleyin
  • Async işleme: Watermark işlemini bir WordPress cron job’una veya Action Scheduler’a devrederek upload sürecini hızlandırabilirsiniz
  • Orijinali yedekle: Watermark eklemeden önce orijinal dosyayı _original suffix’iyle kaydedin, böylece watermark’ı kaldırmanız gerektiğinde elinizde kopya olur

WP-CLI ile watermark sistemini test etmek için:

# Test amaciyla tek bir gorseli yeniden isle
wp media regenerate 42 --yes

# PHP memory limitini kontrol et
wp eval 'echo ini_get("memory_limit");'

# Watermark meta verilerini kontrol et
wp post meta get 42 _wm_watermark_applied

Sonuç

WordPress functions.php üzerinden kurulan bu watermark sistemi, ek bir eklentiye ihtiyaç duymadan tam kontrol sağlar. Özellikle WooCommerce mağazalarında ürün fotoğraflarınızı koruma altına almak için son derece etkili bir yöntemdir. Temel GD fonksiyonlarından başlayıp dinamik boyutlandırma, rol bazlı muafiyetler, metin watermark ve toplu işleme gibi özelliklere kadar genişlettiğinizde, piyasadaki pek çok ücretli eklentiden daha esnek bir sistem elde edersiniz.

Dikkat etmeniz gereken en önemli nokta, watermark’ın orijinal dosyayı kalıcı olarak değiştirdiğidir. Bu nedenle toplu işlem yapmadan önce mutlaka bir yedek alın ve test ortamında deneyin. Ayrıca _wm_watermark_applied meta alanını kullanarak hangi görsellerin işlendiğini takip etmek, sisteminizi uzun vadede yönetilebilir kılar.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir