Alle Grafikdateien aus einem Word-Dokument exportieren

Der Dotnet-Doktor  –  2 Kommentare

Mit einem PowerShell-Skript, das man per Kontextmenü aufruft, bekommt man ganz schnell einen Ordner mit allen Grafiken, die sich in einem Word-DOCX befinden.

Neben meinem Hauptberuf als Softwarearchitekt, Berater und Dozent arbeite ich auch für einige Zeitschriften (zum Beispiel iX, dotnetpro und Windows Developer) und Buchverlage (zum Beispiel Carl Hanser, O'Reilly und apress). Einige Verlage wünschen sich, dass ich neben dem Text als Word-Dokument die Abbildungen (Zeichnungen und Screenshots) als getrennte Grafikdatei abliefere, auch wenn alle Abbildungen bereits in dem Word-Dokument enthalten sind.

Früher habe ich die Grafiken immer einzeln getrennt im Dateisystem abgespeichert. Mit dem zunehmenden Druck, auch in meinem Nebenjob immer effizienter werden zu müssen, habe ich mittlerweile auch diesen Schritt per PowerShell automatisiert. Dabei nutze ich nicht VBA und das Word-Objektmodell, sondern die Tatsache, dass in modernen Word-Dateien (.docx) die Grafiken sowieso schon getrennt gespeichert sind. Das DOCX-Format ist lediglich eine ZIP-Datei, in deren Unterordner /word | media die Grafiken vorliegen, auch dann, wenn ich die Grafiken einfach per Copy&Paste übernommen habe, zum Beispiel aus dem Screenshot-Programm SnagIt oder Microsoft Visio.

Das folgende PowerShell-Skript bezieht sich auf eine DOCX-Datei, die als ersten Kommandozeilenparameter dem Skript übergeben wird. Der PowerShell-Code kopiert die DOCX-Datei und gibt ihr dabei die Dateinamenserweiterung .ZIP. Dann wird diese .ZIP-Datei in einem temporären Ordner extrahiert und der Ordner /temp//word/media wird in den Zielordner /img kopiert. Danach können die .ZIP-Datei und der /temp-Ordner gelöscht werden.

Write-Host "Extract all images from a Word document into folder /img"
Write-Host "Author: Dr. Holger Schwichtenberg, wwww.IT-Visions.de, 2018"

try
{
$ErrorActionPreference = "Stop"; #Make all errors terminating
$path = $args[0] # Get path to .DOCX from Script arguments
Write-Host "DOCX: $path"
$file = Get-Item $path
$filezipname = $file.Name + ".zip"
$filezippath = "$($file.Directory)\$filezipname"
Write-host "Copy $path to ZIP..." -ForegroundColor Yellow
Copy-Item $path $filezippath
Write-host "Extract ZIP to temp folder..." -ForegroundColor Yellow
if (test-path "$($file.Directory)\temp") { Write-Error "Temp exists!"; }
Expand-Archive $filezippath -DestinationPath "$($file.Directory)\temp"
Write-host "Create img folder and copy images..." -ForegroundColor Yellow
md "$($file.Directory)\Img\" -Force
Copy-Item "$($file.Directory)\temp\word\media\*" "$($file.Directory)\Img\" -verbose
Write-host "Cleanup..." -ForegroundColor Yellow
rd $filezippath -ErrorAction Stop
rd "$($file.Directory)\temp" -Recurse
Write-host "Done!" -ForegroundColor Green
}
catch{
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor red
Read-Host "Press ENTER to exit"
}

Das Skript dann jedes Mal an der Kommandozeilen mit dem entsprechenden Pfad zur DOCX-Datei aufzurufen, ist möglich, macht aber keinen Spaß. Also legen wir doch einen Kontextmenüeintrag im Windows Explorer mit einer Registry-Datei an. Dabei muss man den Standort der .ps1-Datei in die letzte Zeile eintragen.

Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Word.Document.12\shell\Extract Images using PowerShell]
[HKEY_CLASSES_ROOT\Word.Document.12\shell\Extract Images using PowerShell\command]
@="powershell.exe -File \"C:\\Scripts\\ExtractImgesFromDOCX.ps1\" \"%1\""

Natürlich kann man auch das noch besser automatisieren. Das Skript kann sich selbst in die Registry eintragen, wenn es "elevated" aufgerufen wird. Dazu würde man das obige PowerShell-Skript etwas erweitern.

Write-Host "Extract all images from a Word document into folder /img"
Write-Host "Author: Dr. Holger Schwichtenberg, wwww.IT-Visions.de, 2018"
Write-Host "Script: $PSScriptRoot\$($MyInvocation.MyCommand.Name)"

# Register "Extract Images" command for DOCX
if ((New-Object
Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
{
Write-Host "Registering this script in Registry..."
New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT
md "HKCR:\Word.Document.12\shell\Extract Images using PowerShell\" -Force
md "HKCR:\Word.Document.12\shell\Extract Images using PowerShell\command" -Force
$registryPath = "HKCR:\Word.Document.12\shell\Extract Images using PowerShell\Command"
$Name = "(Default)"
$value = 'powershell.exe -File "' + $PSScriptRoot +"\" + $MyInvocation.MyCommand.Name + '" "%1"'
New-ItemProperty -Path $registryPath -Name $name -Value $value -PropertyType String -Force | Out-Null
Write-Host "Run script as normal user for processing DOCX!"
return
}

try
{
$ErrorActionPreference = "Stop"; #Make all errors terminating
$path = $args[0] # Get path to .DOCX from Script arguments
Write-Host "DOCX: $path"
$file = Get-Item $path
$filezipname = $file.Name + ".zip"
$filezippath = "$($file.Directory)\$filezipname"
Write-host "Copy $path to ZIP..." -ForegroundColor Yellow
Copy-Item $path $filezippath
Write-host "Extract ZIP to temp folder..." -ForegroundColor Yellow
if (test-path "$($file.Directory)\temp") { Write-Error "Temp exists!"; }
Expand-Archive $filezippath -DestinationPath "$($file.Directory)\temp"
Write-host "Create img folder and copy images..." -ForegroundColor Yellow
md "$($file.Directory)\Img\" -Force
Copy-Item "$($file.Directory)\temp\word\media\*" "$($file.Directory)\Img\" -verbose
Write-host "Cleanup..." -ForegroundColor Yellow
rd $filezippath -ErrorAction Stop
rd "$($file.Directory)\temp" -Recurse
Write-host "Done!" -ForegroundColor Green
}
catch{
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor red
Read-Host "Press ENTER to exit"
}