はじまり
こいつらwebp形式と来たか・・・
それらをPDFにまとめたいな。
DwebpとSystem.Drawing名前空間でPDFに変換可能なJPGを作る。
今回は、複数枚のwebp画像をPDFとして一つにまとめる処理を実装する流れを紹介します。断片化したwebpをPDFにまとめることで、ストレージに対してデフラグを行うことが狙いです。
PDFへの変換には「iTextSharp」を利用していきますが、以前に、Cwebpで圧縮したJPGがiTextSharpでPDFに変換することが出来なかったため、今回の「dwebp」ツールを使った処理でも同様に、PDFに変換可能なJPGは作成できないと仮定します。
そこで今回は、Dwebpツールでwebpをpngに伸長・変換して、.NET FrameworkのSystem.Drawing名前空間でそのpngをjpgに変換する。そして、そのjpg画像をPDFにまとめるという流れで、処理を実装していきます。それではやっていきましょう。
全体的な処理のコード
Dwebpツールでwebpをpngに変換して、System.Drawing名前空間でそのpngをjpgに変換する。
その処理をこの関数で定義します。
function Optimize-ImagesWithDecompressing() {
<#
.SYNOPSIS
Converts multiple WebP images to JPEG format with specified quality.
.DESCRIPTION
This function converts WebP images to JPEG format. It first decompresses each WebP to PNG using dwebp, then compresses the PNG to JPEG using the specified quality.
.PARAMETER pathList
An array of full file paths to the WebP images to be converted.
.PARAMETER suffix
A string suffix to append to the filename before the .png extension during the decompression stage. This suffix is not used in final jpeg filename.
.PARAMETER quality
An integer representing the JPEG compression quality (0-100). 100 is highest quality.
.EXAMPLE
$images = Get-ChildItem -Path "C:\path\to\images" -Filter "*.webp"
Optimize-ImagesWithDecompressing -pathList $images.FullName -suffix "decompressed" -quality 85
.INPUTS
System.Array, System.String, System.Int32
.OUTPUTS
None. Output files are written to disk.
.NOTES
Requires the dwebp tool and System.Drawing assembly.
.LINK
dwebp, [System.Drawing.Image]
#>
[CmdletBinding()]
param (
[array]$pathList,
[string]$suffix,
[int]$quality
)
# Add-Type -AssemblyName System.Drawing
foreach ($file in $pathList) {
$extension = (Get-Item $file.FullName).Extension;
$outputImg1 = "{0}_{1:D2}{2}" -f $file.FullName.Substring(0, $file.FullName.Length - $extension.Length), $suffix, ".png";
Write-Output "<1st Source File Name: $file>";
Optimize-ImageWithDecompressing -ImagePath $file -OutputPath $outputImg1;
$extension = (Get-Item $outputImg1).Extension;
$outputImg2 = "{0}{1}" -f $outputImg1.Substring(0, $outputImg1.Length - $extension.Length), ".jpg";
Write-Output "<2nd Source File Name: $outputImg>";
Optimize-ImageWithCompressing -ImagePath $outputImg1 -OutputPath $outputImg2 -Quality $quality;
$updatedCount++;
Write-Output "----------------";
};
Write-Output("{0} .jpg images converted." -f $updatedCount);
Write-Output "----------------";
}
$targetFolder = (Get-Location).Path;
$imgPathList = Get-ChildItem -Path $targetFolder | Where-Object { $_.Extension -in @(".webp") };
$suffix = "compressed";
$quality = [int]($Args[0]); # 0-100
Optimize-ImagesWithDecompressing $imgPathList $suffix $quality;
$tmp = Read-Host "Input 'y' if you wanna move original images......";
If ($tmp -eq "y") {
Move-Item -Path $imgPathList -Destination .\5_original_files;
$imgPathList = Get-ChildItem -Path $targetFolder | Where-Object { $_.Extension -in @(".png") };
Move-Item -Path $imgPathList -Destination .\5_original_files;
}
Dwebpツールでwebpをpngに変換する。
「Libwebp」というGoogle製のパッケージに、「Dwebp」という機能があるので、その機能を使ってwebpをpngに変換していきます。
変換処理のコードは以下の通りです。実質1行で事足りますが。
function Optimize-ImageWithDecompressing {
# Load System.Drawing assembly
Add-Type -AssemblyName System.Drawing
try {
dwebp $ImagePath -o $OutputPath;
Write-Host "Image decompressed and saved to: $OutputPath";
}
catch {
Write-Error "An error occurred: $_"
}
finally {
if ($image) {
$image.Dispose()
}
}
}
System.Drawing名前空間を使って圧縮する。
そしたら次に、Dwebpで変換したPNGファイルをJPGファイルとして圧縮していくことにしましょう。この処理を噛ませることで、PDFのサイズを小さく出来ます。
.NET FrameworkのSystem.Drawing名前空間を使って、JPGを作っていきます。Image.Save メソッドで保存する画像ファイルの圧縮率を指定出来るエンコーダを引数に渡せば、JPGを圧縮できそうです。
その公式のコードを参考に、PowerShellとして書き直します。そして、以下の関数を呼び出せばJPGに圧縮することが出来ました。
function Optimize-ImageWithCompressing {
param (
[string]$ImagePath,
[string]$OutputPath,
[int]$Quality # 0-100, 100 is highest quality (least compression)
)
# Load System.Drawing assembly
Add-Type -AssemblyName System.Drawing
try {
# Load the image
$image = [System.Drawing.Image]::FromFile($ImagePath)
# Create an EncoderParameter object for quality
$qualityParam = New-Object System.Drawing.Imaging.EncoderParameter(
[System.Drawing.Imaging.Encoder]::Quality, $Quality
)
# Get the JPEG codec
$jpegCodec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageDecoders() | Where-Object { $_.FormatID -eq [System.Drawing.Imaging.ImageFormat]::Jpeg.Guid }
# Create an EncoderParameters object
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = $qualityParam
# Save the image with the specified quality
$image.Save($OutputPath, $jpegCodec[0], $encoderParams)
Write-Host "Image compressed and saved to: $OutputPath"
}
catch {
Write-Error "An error occurred: $_"
}
finally {
if ($image) {
$image.Dispose()
}
}
}
iTextSharpでPDFにまとめる。
そしたら次に、圧縮したJPGをiTextSharpでPDFに変換します。
function Merge-ImagesIntoPdfWithITextSharp([array]$imgPathList, [string]$iTextSharpPath) {
Unblock-File -Path $iTextSharpPath;
# [System.Reflection.Assembly]::LoadFrom($iTextSharpPath);
# Add-Type -Path $iTextSharpPath
Add-Type -LiteralPath $iTextSharpPath
# $error[0].Exception.GetBaseException().LoaderExceptions
$ext = ".pdf";
$pdfName = "{0}\{1}{2}" -f $imgPathList[0].DirectoryName, $imgPathList[0].BaseName, $ext;
Write-Output $imgPathList;
Write-Host ("{0}: decided PDF file name`n" -f $MyInvocation.MyCommand.Name);
Write-Output $pdfName;
$doc = New-Object iTextSharp.text.Document;
$pdfWriter = [iTextSharp.text.pdf.PdfWriter]::GetInstance($doc, [System.IO.File]::Create($pdfName))
$doc.Open()
# Get image files to add into PDF.
foreach ($path in $imgPathList) {
Write-Output $path;
Write-Output $path.GetType().FullName;
Write-Output $path.FullName.GetType().FullName;
Write-Host ("{0}: adding an image into a PDF...`n" -f $MyInvocation.MyCommand.Name);
$image = [iTextSharp.text.Image]::GetInstance($path.FullName);
# Scale images to each page size.
$doc.SetPageSize($image);
$doc.NewPage();
$image.SetAbsolutePosition(0, 0);
# $image.ScaleToFit($doc.PageSize.Width, $doc.PageSize.Height);
$image.Alignment = [iTextSharp.text.Image]::ALIGN_CENTER;
$doc.Add($image);
}
$doc.Close();
$pdfWriter.Close();
Write-Host ("{0}: completed adding images into a PDF`n" -f $MyInvocation.MyCommand.Name);
}
今までの一連の処理を行えば、複数のWEBP画像を1つのPDFにまとめることが出来るようになりました! めでたしめでたし。
ちなみに、Dwebpを飛ばすと失敗する。
今回、以下の流れで処理を実装しました。
- Dwebpでwebpをpngに変換する。
- System.Drawing名前空間でpngをjpgに変換する。
- iTextSharpでjpgをpdfに変換する。
ちなみに、上記の工程で1の工程をすっ飛ばして、System.Drawing名前空間でwebpをjpgに変換しようとすると失敗します。
Optimize-ImageWithDecompressing : An error occurred: “1” 個の引数を指定して “FromFile” を呼び出し中に例外が発生しました : “メモリが不足しています。”
発生場所 C:\Users\…\Z5-9_decompress_webp_to_jpg_with_sys_draw.ps1:88 文字:5 + Optimize-ImageWithDecompressing -ImagePath $file -OutputPath $out …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Optimize-ImageWithDecompressing
まとめ
今回は、iTextSharpをPowerShellで叩いて、複数枚のWebp画像を一つのPDFにまとめる処理を実装する試みでした。
以下が、本記事のまとめです。
- iTextSharpパッケージで複数の画像ファイルをPDFにまとめることが可能である。JPG画像でまとめればサイズをより減らせる。
- Libwebpパッケージのdwebpツールで、webp画像ファイルをPNG画像に変換することが可能である。
- System.Drawing名前空間でPNG画像をJPG画像に変換することが可能である。しかし、WEBP画像を変換しようとすると失敗する。
Webpファイルは高品質で低サイズの画像ファイルを構成できますが、フラグメンテーションを放置するのは勿体ないとも思います。そんな時に使えそうなツールでした。
その他のPowerShell関連の記事
おしまい
こんなこともあるんだなぁ
これでPDFにまとめられます。
以上になります!
コメント