<#
.\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): $_"
}
}