EXIF-Daten aus Bildern entfernen

<#
 .\Strip-Exif-KeepStructure.ps1 -Recurse
Input: aktueller Ordner (.)
Output: .\_stripped (im selben Ordner), behält Unterordner-Struktur
Entfernt EXIF/Metadaten durch Neu-Encoding (JPEG/PNG/TIFF/BMP/GIF).
#>

param(
    [switch]$Recurse,
    [switch]$Overwrite
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

Add-Type -AssemblyName System.Drawing

$inputDir  = (Get-Location).Path
$outputDir = Join-Path $inputDir "_stripped"

if (-not (Test-Path -LiteralPath $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir | Out-Null
}

function Ensure-Dir([string]$Path) {
    if (-not (Test-Path -LiteralPath $Path)) {
        New-Item -ItemType Directory -Path $Path | Out-Null
    }
}

function Save-WithoutMetadata {
    param(
        [Parameter(Mandatory=$true)][string]$Source,
        [Parameter(Mandatory=$true)][string]$Dest
    )

    $srcExt = [IO.Path]::GetExtension($Source).ToLowerInvariant()

    $img = $null
    $bmp = $null
    try {
        $img = [System.Drawing.Image]::FromFile($Source)

        # Neue Bitmap -> Metadaten werden nicht übernommen
        $bmp = New-Object System.Drawing.Bitmap($img.Width, $img.Height, [System.Drawing.Imaging.PixelFormat]::Format24bppRgb)
        $g = [System.Drawing.Graphics]::FromImage($bmp)
        $g.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
        $g.InterpolationMode  = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
        $g.SmoothingMode      = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
        $g.DrawImage($img, 0, 0, $img.Width, $img.Height)
        $g.Dispose()

        switch ($srcExt) {
            ".png"  { $format = [System.Drawing.Imaging.ImageFormat]::Png }
            ".gif"  { $format = [System.Drawing.Imaging.ImageFormat]::Gif }
            ".bmp"  { $format = [System.Drawing.Imaging.ImageFormat]::Bmp }
            ".tif"  { $format = [System.Drawing.Imaging.ImageFormat]::Tiff }
            ".tiff" { $format = [System.Drawing.Imaging.ImageFormat]::Tiff }
            default { $format = [System.Drawing.Imaging.ImageFormat]::Jpeg } # jpg/jpeg
        }

        # JPEG Qualität (nur bei JPEG)
        if ($format.Guid -eq ([System.Drawing.Imaging.ImageFormat]::Jpeg).Guid) {
            $codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() |
                Where-Object { $_.FormatID -eq ([System.Drawing.Imaging.ImageFormat]::Jpeg).Guid }

            $encParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
            $encParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, 92L)

            $bmp.Save($Dest, $codec, $encParams)
        }
        else {
            $bmp.Save($Dest, $format)
        }
    }
    finally {
        if ($bmp) { $bmp.Dispose() }
        if ($img) { $img.Dispose() }
    }
}

# Dateien sammeln
$opt = @{}
if ($Recurse) { $opt.Recurse = $true }

$files = Get-ChildItem -LiteralPath $inputDir -File @opt |
    Where-Object { $_.Extension -match '^\.(jpg|jpeg|png|tif|tiff|bmp|gif)$' } |
    # Wichtig: Alles in _stripped selbst ignorieren (sonst doppelte Verarbeitung)
    Where-Object { $_.FullName -notlike (Join-Path $outputDir '*') }

foreach ($f in $files) {
    # relativen Pfad zum inputDir berechnen
    $rel = [IO.Path]::GetRelativePath($inputDir, $f.FullName)

    # Zielpfad unter _stripped: gleicher rel. Pfad
    $dest = Join-Path $outputDir $rel

    # Zielordner sicherstellen
    $destFolder = [IO.Path]::GetDirectoryName($dest)
    Ensure-Dir $destFolder

    if ((Test-Path -LiteralPath $dest) -and (-not $Overwrite)) {
        Write-Warning "Ziel existiert, überspringe (nutze -Overwrite): $dest"
        continue
    }

    try {
        Save-WithoutMetadata -Source $f.FullName -Dest $dest
        Write-Host "OK: $dest"
    }
    catch {
        Write-Error "FEHLER bei $($f.FullName): $_"
    }
}