Skip to content

Instantly share code, notes, and snippets.

@futuremotiondev
Created January 29, 2025 13:03
Show Gist options
  • Save futuremotiondev/407fbe99ccfb2b0836924d66921bc93b to your computer and use it in GitHub Desktop.
Save futuremotiondev/407fbe99ccfb2b0836924d66921bc93b to your computer and use it in GitHub Desktop.
Problems with converting multiple PNGs to 32-Bit ICO using ImageMagick in PowerShell.
using namespace System.Collections.Generic
using namespace System.Collections.Concurrent
using namespace System.IO
function Convert-SVGToICOWorking {
[CmdletBinding(DefaultParameterSetName = "Path")]
param (
[Parameter(
Mandatory,
Position = 0,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
ParameterSetName = "Path",
HelpMessage="Path to one or more SVG files."
)]
[SupportsWildcards()]
[ValidateNotNullOrEmpty()]
[String[]] $Path,
[Parameter(
Mandatory,
Position = 0,
ValueFromPipelineByPropertyName,
ParameterSetName = "LiteralPath",
HelpMessage="Literal path to one or more SVG files."
)]
[ValidateScript({$_ -notmatch '[\?\*]'},
ErrorMessage = "Wildcard characters *, ? are not acceptable with -LiteralPath")]
[ValidateScript({[System.IO.Path]::IsPathRooted($_)},
ErrorMessage = "Relative paths are not allowed in -LiteralPath.")]
[Alias('PSPath')]
[ValidateNotNullOrEmpty()]
[String[]] $LiteralPath,
[Parameter(HelpMessage = "If set, ICO files will be placed in a subfolder of the SVGs containing directory.")]
[Switch] $OutputInSubfolder,
[Parameter(HelpMessage = "The name of the subfolder to place ICO files if -OutputInSubfolder is set.")]
[String] $SubfolderName = 'ICO Conversion',
[Parameter(HelpMessage = "The number of simultaneous threads to use during conversion.")]
[Int32] $MaxThreads = 20
)
begin {
$rsvgCmd = Get-Command rsvg-convert.exe -EA 0
if(-not$rsvgCmd){
Write-Error "rsvg-convert.exe cannot be found in PATH. Aborting"
return
}
$magickCmd = Get-Command magick.exe -EA 0
if(-not$magickCmd){
Write-Error "magick.exe (ImageMagick) cannot be found in PATH. Aborting"
return
}
$svgList = [List[FileInfo]]@()
}
process {
$resolvedPaths = if($PSBoundParameters['Path']) {
$Path | Get-Item -Force
} elseif($PSBoundParameters['LiteralPath']) {
Get-Item -LiteralPath $LiteralPath -Force
}
$resolvedPaths | % {
$fileName = $_.FullName
if (Test-Path -LiteralPath $fileName -PathType Container) {
$gciSplat = @{
LiteralPath = $fileName
Force = $true
Recurse = $true
ErrorAction = 'SilentlyContinue'
File = -File
Filter = '*.svg'
}
$childItems = Get-ChildItem @gciSplat
if($childItems){
foreach ($item in $childItems) {
$null = $svgList.Add($item)
}
}
} elseif(Test-Path -LiteralPath $fileName -PathType Leaf) {
if($_.Extension -eq '.svg'){
$null = $svgList.Add($_)
}
}
}
}
end {
$tempDirDict = [ConcurrentDictionary[String,String]]::new()
if($PSBoundParameters['OutputInSubfolder']){
$tempDirName = $SubfolderName
} else {
$tempDirName = ((New-Guid).Guid)
}
$svgList | ForEach-Object -Parallel {
$curSvg = $_.FullName
$curSvgBaseName = $_.BaseName
$curSvgDir = $_.Directory
$tempDirName = $Using:tempDirName
$tempDirDict = $using:tempDirDict
$rsvgCmd = $Using:rsvgCmd
$magickCmd = $Using:magickCmd
$tempFolder = Join-Path -Path $curSvgDir -ChildPath $tempDirName
if(-not($tempFolder | Test-Path -PathType Container)){
New-Item -Path $tempFolder -ItemType Directory -Force | Out-Null
}
$icoSizes = @(16, 20, 24, 32, 48, 64, 96, 128, 256)
foreach ($size in $icoSizes) {
$rsvgParams = "-a", "-w", $size, "-h", $size, "-f", "png", $curSvg, "-o", "$tempFolder\$curSvgBaseName-$size.png"
& $rsvgCmd $rsvgParams | Out-Null
}
$magickCmd = Get-Command magick.exe -CommandType Application
$printArgs = Get-Command print_argv.exe -CommandType Application
# COMMAND 1: Works but throws error: magick.exe: Cannot write image with defined
# png:bit-depth or png:color-type. `' @ warning/png.c/MagickPNGWarningHandler/1526.
& $printArgs $($icoSizes.ForEach{"$tempFolder\$curSvgBaseName-$_.png"}) "$tempFolder\$curSvgBaseName.ico"
& $magickCmd $($icoSizes.ForEach{"$tempFolder\$curSvgBaseName-$_.png"}) "$tempFolder\$curSvgBaseName.ico"
# COMMAND 2: Works but throws Inkscape errors: (org.inkscape.Inkscape:13820):
# WARNING **: 06:50:22.942: Failed to find resource file 'svg2ico.py'. Looked in:
# C:\Users\username\AppData\Roaming\inkscape\extensions\svg2ico.py
# & $printArgs -background none $curSvg -define icon:auto-resize="16,20,24,32,48,64,96,128,256" -depth 32 "$tempFolder\$curSvgBaseName.ico"
# & $magickCmd -background none $curSvg -define icon:auto-resize="16,20,24,32,48,64,96,128,256" -depth 32 "$tempFolder\$curSvgBaseName.ico"
if (-not ($tempDirDict.Values -contains $tempFolder)) {
$null = $tempDirDict.TryAdd($((New-Guid).Guid), $tempFolder) | Out-Null
}
} -ThrottleLimit $MaxThreads
$tempDirDict.GetEnumerator() | % {
$tempDir = Get-Item -LiteralPath $($_.Value) -Force
$tempDirFull = $tempDir.FullName
$tempDirParent = $tempDir.Parent
Get-ChildItem $tempDirFull -Filter '*.png' -Recurse -Force | Remove-Item -Force
if(-not($PSBoundParameters['OutputInSubfolder'])){
$icoFiles = Get-ChildItem -LiteralPath $tempDirFull -Filter '*.ico' -Recurse -Force
$icoFiles | % {
$curIcoFullname = $_.FullName
$icoDestPath = [System.IO.Path]::Combine($tempDirParent, $_.Name)
$icoDestPath = Get-UniqueNameIfDuplicate -LiteralPath $icoDestPath
Move-Item -LiteralPath $curIcoFullname -Destination $icoDestPath -Force | Out-Null
}
Remove-Item -LiteralPath $tempDirFull -Recurse -Force | Out-Null
}
}
}
}
Convert-SVGToICOWorking -LiteralPath "C:\Icons\00 Apps\Chrome\00 Source\FM Chrome Light Black 03.svg" -OutputInSubfolder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment