PSStringToQRCode.psm1
# PSStringToQRCode.psm1 # # CHANGES # 1.0.1 - 31/07/2024 - Initial development # 1.0.2 - 24/08/2025 - Added switch to output image as a stream, removed unnessasary output, added alias so that PSStringToQRCode = Convert-StringToQR, so that its easier to find cmdlet after installing module. function Convert-StringToQR { [CmdletBinding()] [OutputType([System.IO.Stream])] param( [Parameter(Mandatory, ValueFromPipeline)][string]$Text, [int]$Height = 600, [int]$Width = 600, [string]$SaveImageOutput, [switch]$StreamOutput ) begin { $moduleBase = (Get-Module PSStringToQRCode).ModuleBase Add-Type -Path (Join-Path $moduleBase 'Net.Codecrete.QrCodeGenerator\Net.Codecrete.QrCodeGenerator.dll') Add-Type -Path (Join-Path $moduleBase 'SkiaSharp\SkiaSharp.dll') Add-Type -Path (Join-Path $moduleBase 'Svg.Skia\Svg.Skia.dll') } process { #=== Build SVG in-memory ===# $qrCode = [Net.Codecrete.QrCodeGenerator.QrCode]::encodeText($Text, [Net.Codecrete.QrCodeGenerator.QrCode+Ecc]::MEDIUM) $qrCodeSvg = $qrCode.ToSvgString(4) $memoryStream = [System.IO.MemoryStream]::new() try { $svgBytes = [System.Text.Encoding]::UTF8.GetBytes($qrCodeSvg) [void]$memoryStream.Write($svgBytes, 0, $svgBytes.Length) [void]$memoryStream.Seek(0, [System.IO.SeekOrigin]::Begin) $svg = [Svg.Skia.SKSvg]::new() try { # IMPORTANT: suppress pipeline output from Load() [void]$svg.Load($memoryStream) $picture = $svg.Picture $bounds = $picture.CullRect #=== Render SVG to bitmap ===# $bmp = [SkiaSharp.SKBitmap]::new([int]$Width, [int]$Height) $canvas = [SkiaSharp.SKCanvas]::new($bmp) try { $canvas.Clear([SkiaSharp.SKColors]::White) $scaleX = $Width / $bounds.Width $scaleY = $Height / $bounds.Height $scale = [Math]::Min($scaleX, $scaleY) $tx = ($Width - $bounds.Width * $scale) / 2 $ty = ($Height - $bounds.Height * $scale) / 2 $canvas.Scale($scale, $scale) | Out-Null $canvas.Translate($tx / $scale, $ty / $scale) | Out-Null $canvas.DrawPicture($picture) | Out-Null $canvas.Flush() | Out-Null #=== Encode to PNG ===# $image = [SkiaSharp.SKImage]::FromBitmap($bmp) try { $data = $image.Encode([SkiaSharp.SKEncodedImageFormat]::Png, 100) try { #--- Output selection ---# if ($StreamOutput.IsPresent) { # return a MemoryStream containing the PNG $pngStream = [System.IO.MemoryStream]::new() $data.SaveTo($pngStream) [void]$pngStream.Seek(0, [System.IO.SeekOrigin]::Begin) return $pngStream } elseif ($SaveImageOutput) { # save to file (without printing anything) $fs = [System.IO.File]::Open($SaveImageOutput, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::None) try { $data.SaveTo($fs) $fs.Flush() } finally { $fs.Dispose() } } else { # show a simple Windows Forms preview Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $pngStream = [System.IO.MemoryStream]::new() $data.SaveTo($pngStream) [void]$pngStream.Seek(0, [System.IO.SeekOrigin]::Begin) $form = New-Object Windows.Forms.Form $form.Text = "QR Code [$Text]" $form.Width = [int]$Width + 16 $form.Height = [int]$Height + 39 $form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen $pb = New-Object Windows.Forms.PictureBox $pb.Width = [int]$Width $pb.Height = [int]$Height $pb.Image = [System.Drawing.Bitmap]::FromStream($pngStream) $pb.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::StretchImage $form.Controls.Add($pb) [void]$form.ShowDialog() # dispose UI image stream after closing the form $pb.Image.Dispose() $pngStream.Dispose() } } finally { $data.Dispose() } } finally { $image.Dispose() } } finally { $canvas.Dispose() $bmp.Dispose() } } finally { $svg.Dispose() } } finally { $memoryStream.Dispose() } } } New-Alias -Name PSStringToQRCode -Value Convert-StringToQR |