ImagePlayground.psm1

# Get library name, from the PSM1 file name
$LibraryName = 'ImagePlayground.PowerShell'
$Library = "$LibraryName.dll"
$Class = "$LibraryName.Initialize"

$AssemblyFolders = Get-ChildItem -Path $PSScriptRoot\Lib -Directory -ErrorAction SilentlyContinue

# Lets find which libraries we need to load
$Default = $false
$Core = $false
$Standard = $false
foreach ($A in $AssemblyFolders.Name) {
    if ($A -eq 'Default') {
        $Default = $true
    } elseif ($A -eq 'Core') {
        $Core = $true
    } elseif ($A -eq 'Standard') {
        $Standard = $true
    }
}
if ($Standard -and $Core -and $Default) {
    $FrameworkNet = 'Default'
    $Framework = 'Standard'
} elseif ($Standard -and $Core) {
    $Framework = 'Standard'
    $FrameworkNet = 'Standard'
} elseif ($Core -and $Default) {
    $Framework = 'Core'
    $FrameworkNet = 'Default'
} elseif ($Standard -and $Default) {
    $Framework = 'Standard'
    $FrameworkNet = 'Default'
} elseif ($Standard) {
    $Framework = 'Standard'
    $FrameworkNet = 'Standard'
} elseif ($Core) {
    $Framework = 'Core'
    $FrameworkNet = ''
} elseif ($Default) {
    $Framework = ''
    $FrameworkNet = 'Default'
} else {
    Write-Error -Message 'No assemblies found'
}
if ($PSEdition -eq 'Core') {
    $LibFolder = $Framework
} else {
    $LibFolder = $FrameworkNet
}

try {
    $ImportModule = Get-Command -Name Import-Module -Module Microsoft.PowerShell.Core

    if (-not ($Class -as [type])) {
        & $ImportModule ([IO.Path]::Combine($PSScriptRoot, 'Lib', $LibFolder, $Library)) -ErrorAction Stop
    } else {
        $Type = "$Class" -as [Type]
        & $importModule -Force -Assembly ($Type.Assembly)
    }
} catch {
    if ($ErrorActionPreference -eq 'Stop') {
        throw
    } else {
        Write-Warning -Message "Importing module $Library failed. Fix errors before continuing. Error: $($_.Exception.Message)"
        # we will continue, but it's not a good idea to do so
        # return
    }
}
# Dot source all libraries by loading external file
. $PSScriptRoot\ImagePlayground.Libraries.ps1

function ConvertTo-Image {
    <#
    .SYNOPSIS
    Converts the image to the specified format.
 
    .DESCRIPTION
    Converts the image to the specified format. The output path must include the file extension.
 
    .PARAMETER FilePath
    File path to the image you want to convert.
 
    .PARAMETER OutputPath
    File path to the output image that will be created.
 
    .EXAMPLE
    ConvertTo-Image -FilePath $PSScriptRoot\Samples\LogoEvotec.png -OutputPath $PSScriptRoot\Output\LogoEvotec.jpg
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [parameter(Mandatory)][string] $FilePath,
        [parameter(Mandatory)][string] $OutputPath
    )
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        [ImagePlayground.ImageHelper]::ConvertTo($FilePath, $OutputPath)
    } else {
        Write-Warning -Message "Resize-Image - File $FilePath not found. Please check the path."
    }
}
function Get-Image {
    <#
    .SYNOPSIS
    Gets the image from the file path for further processing and image manipulation.
 
    .DESCRIPTION
    Gets the image from the file path for further processing and image manipulation.
 
    .PARAMETER FilePath
    File path to the image you want to read and manipulate.
 
    .EXAMPLE
    $Image = Get-Image -FilePath $PSScriptRoot\Samples\LogoEvotec.png
    $Image.BlackWhite()
    $Image.BackgroundColor("Red")
    Save-Image -Image $Image -Open -FilePath $PSScriptRoot\Output\LogoEvotecChanged.png
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [string] $FilePath
    )

    if (-not (Test-Path -LiteralPath $FilePath)) {
        Write-Warning -Message "Get-Image - File $FilePath not found. Please check the path."
        return
    }
    $Image = [ImagePlayground.Image]::Load($FilePath)
    $Image
}
function Get-ImageBarCode {
    <#
    .SYNOPSIS
    Gets bar code from image
 
    .DESCRIPTION
    Gets bar code from image
 
    .PARAMETER FilePath
    File path to image to be processed for bar code reading
 
    .EXAMPLE
    Get-ImageBarCode -FilePath "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg"
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [string] $FilePath
    )
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        $BarCode = [ImagePlayground.BarCode]::Read($FilePath)
        $BarCode
    } else {
        Write-Warning -Message "Get-ImageBarCode - File $FilePath not found. Please check the path."
    }
}
function Get-ImageExif {
    <#
    .SYNOPSIS
    Gets EXIF data from image
 
    .DESCRIPTION
    Gets EXIF data from image.
 
    .PARAMETER FilePath
    File path to image to be processed for Exif Tag reading
 
    .EXAMPLE
    Get-ImageExif -FilePath "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg"
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $FilePath,
        [switch] $Translate
    )
    if (-not (Test-Path $FilePath)) {
        Write-Warning -Message "Get-ImageExif - File not found: $FilePath"
        return
    }
    $Image = Get-Image -FilePath $FilePath
    if ($Translate) {
        $SingleExif = [ordered] @{}
        $Image.Metadata.ExifProfile.Values | ForEach-Object {
            $SingleExif[$_.Tag.ToString()] = $_.Value
        }
        [PSCustomObject] $SingleExif
    } else {
        $Image.Metadata.ExifProfile.Values
    }
}
function Get-ImageQRCode {
    <#
    .SYNOPSIS
    Gets QR code from image
 
    .DESCRIPTION
    Gets QR code from image
 
    .PARAMETER FilePath
    File path to image to be processed for QR code reading
 
    .EXAMPLE
    Get-ImageQRCode -FilePath "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg"
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [string] $FilePath
    )
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        $QRCode = [ImagePlayground.QRCode]::Read($FilePath)
        $QRCode
    } else {
        Write-Warning -Message "Get-ImageQRCode - File $FilePath not found. Please check the path."
    }
}
function Merge-Image {
    <#
    .SYNOPSIS
    Merges two images into a single image
 
    .DESCRIPTION
    Merges two images into a single image
 
    .PARAMETER FilePath
    File path to image to be processed for merging of images. This image will be the base image.
 
    .PARAMETER FilePathToMerge
    File path to image to be merged into the base image.
 
    .PARAMETER FilePathOutput
    File path to output image.
 
    .PARAMETER ResizeToFit
    Resize the image to fit the base image. If not specified, the image will be added as is.
 
    .PARAMETER Placement
    Placement of the image to be merged. Default is bottom. Possible values: Top, Bottom, Left, Right
 
    .EXAMPLE
    Merge-Image -FilePath "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg" -FilePathToMerge "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg" -FilePathOutput "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg" -ResizeToFit -Placement Bottom
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [string] $FilePath,
        [string] $FilePathToMerge,
        [string] $FilePathOutput,
        [switch] $ResizeToFit,
        [ImagePlayground.ImagePlacement] $Placement = [ImagePlayground.ImagePlacement]::Bottom
    )
    if (-not (Test-Path -LiteralPath $FilePath)) {
        Write-Warning -Message "Merge-Image - File $FilePath not found. Please check the path."
        return
    }
    if (-not (Test-Path -LiteralPath $FilePathToMerge)) {
        Write-Warning -Message "Merge-Image - File $FilePathToMerge not found. Please check the path."
        return
    }
    [ImagePlayground.ImageHelper]::Combine($FilePath, $FilePathToMerge, $FilePathOutput, $ResizeToFit.IsPresent, $Placement)
}
function New-ImageBarCode {
    [cmdletBinding()]
    param(
        [parameter(Mandatory)][ImagePlayground.BarCode+BarcodeTypes] $Type,
        [parameter(Mandatory)][string] $Value,
        [parameter(Mandatory)][string] $FilePath
    )
    [ImagePlayground.BarCode]::Generate($Type, $Value, $filePath)
}
function New-ImageChart {
    [cmdletBinding()]
    param(
        [scriptblock] $ChartsDefinition,
        [int] $Width = 600,
        [int] $Height = 400,
        [string] $FilePath,
        [switch] $Show
    )
    $ValueHash = [ordered] @{}
    $Values = [System.Collections.Generic.List[double]]::new()
    $Labels = [System.Collections.Generic.List[string]]::new()
    $Positions = [System.Collections.Generic.List[int]]::new()
    $Plot = [ScottPlot.Plot]::new($Width, $Height)
    $Position = 0

    if ($ChartsDefinition) {
        $OutputDefintion = & $ChartsDefinition
        foreach ($Definition in $OutputDefintion) {
            if ($Definition.ObjectType -eq 'Bar') {
                $Type = 'Bar'
                for ($i = 0; $i -lt $Definition.Value.Count; $i++) {
                    if (-not $ValueHash["$i"]) {
                        $ValueHash["$i"] = [System.Collections.Generic.List[double]]::new()
                    }
                    $ValueHash["$i"].Add($Definition.Value[$i])
                }
                $Labels.Add($Definition.Name)
            } elseif ($Definition.ObjectType -eq 'Line') {
                $Type = 'Line'

                #if (-not $ValueHash["$Position"]) {
                # $ValueHash["$Position"] = [System.Collections.Generic.List[double]]::new()
                #}
                $ValueHash["$($Definition.Name)"] = $Definition.Value
            } elseif ($Definition.ObjectType -eq 'Pie') {
                $Type = 'Pie'
                $Values.Add($Definition.Value)
                $Labels.Add($Definition.Name)
            } elseif ($Definition.ObjectType -eq 'Radial') {
                $Type = 'Radial'
                $Values.Add($Definition.Value)
                $Labels.Add($Definition.Name)
            }
            $Positions.Add($Position)
            $Position++
        }
    }
    if ($Type -eq 'Bar') {
        #void SetAxisLimits(System.Nullable[double] xMin, System.Nullable[double] xMax, System.Nullable[double] yMin, System.Nullable[double] yMax, int xAxisIndex, int yAxisIndex)
        #void SetAxisLimits(ScottPlot.AxisLimits limits, int xAxisIndex, int yAxisIndex)
        # adjust axis limits so there is no padding below the bar graph
        # $Plot.SetAxisLimits(yMin: 0);
        if ($ValueHash) {
            foreach ($Value in $ValueHash.Keys) {
                $null = $Plot.AddBar($ValueHash[$Value])
            }
        }
        if ($Labels) {
            $null = $Plot.XTicks($Positions, $Labels)
        }
    } elseif ($Type -eq 'Line') {
        if ($ValueHash) {
            foreach ($Value in $ValueHash.Keys) {
                #ScottPlot.Plottable.SignalPlot AddSignal(double[] ys, double sampleRate = 1, System.Nullable[System.Drawing.Color] color = default, string label = null)
                #ScottPlot.Plottable.SignalPlotGeneric[T] AddSignal[T](T[] ys, double sampleRate = 1, System.Nullable[System.Drawing.Color] color = default, string label = null)
                $null = $Plot.AddSignal($ValueHash[$Value], 1, $null, $Value)
            }
        }
        $null = $Plot.Legend($true, 7)
    } elseif ($Type -eq 'Pie') {
        $PieChart = $Plot.AddPie($Values)
        $PieChart.SliceLabels = $Labels
        $PieChart.ShowLabels = $true
    } elseif ($Type -eq 'Radial') {
        $RadialChart = $Plot.AddRadialGauge($Values)
        $RadialChart.Labels = $Labels
        $null = $Plot.Legend($true, 7)
    }


    if (-not $FilePath) {
        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).png")
        Write-Warning -Message "New-ImageChart - No file path specified, saving to $FilePath"
    }
    try {
        $null = $Plot.SaveFig($FilePath)
        if ($Show) {
            Invoke-Item -LiteralPath $FilePath
        }
    } catch {
        Write-Warning -Message "New-ImageChart - Error creating image chart $($_.Exception.Message)"
    }
}
function New-ImageChartBar {
    [cmdletBinding()]
    param(
        [alias('Label')][string] $Name,
        [Array] $Value
    )
    [PSCustomObject] @{
        ObjectType = 'Bar'
        Name       = $Name
        Value      = $Value
    }
}
function New-ImageChartBarOptions {
    [cmdletBinding()]
    param(
        [switch] $ShowValuesAboveBars
    )

    [PSCustomObject] @{
        ObjectType          = 'BarOptions'
        ShowValuesAboveBars = $ShowValuesAboveBars.IsPresent
    }
}
function New-ImageChartLine {
    [CmdletBinding()]
    param(
        [alias('Label')][string] $Name,
        [Array] $Value
    )
    [PSCustomObject] @{
        ObjectType = 'Line'
        Name       = $Name
        Value      = $Value
    }
}
function New-ImageChartPie {
    [cmdletbinding()]
    param(
        [alias('Label')][string] $Name,
        [double] $Value
    )
    [PSCustomObject] @{
        ObjectType = 'Pie'
        Name       = $Name
        Value      = $Value
    }
}
function New-ImageChartRadial {
    [cmdletbinding()]
    param(
        [alias('Label')][string] $Name,
        [double] $Value
    )
    [PSCustomObject] @{
        ObjectType = 'Radial'
        Name       = $Name
        Value      = $Value
    }
}
function New-ImageQRCode {
    <#
    .SYNOPSIS
    Creates QR code
 
    .DESCRIPTION
    Creates QR code
 
    .PARAMETER Content
    Content to be encoded in QR code
 
    .PARAMETER FilePath
    File path to where the image with QR code should be saved.
 
    .EXAMPLE
    New-ImageQRCode -Content 'https://evotec.xyz' -FilePath "$PSScriptRoot\Samples\QRCode.png" -Verbose
 
    .NOTES
    General notes
    #>

    [Alias('New-QRCode')]
    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $Content,
        [Parameter(Mandatory)][string] $FilePath
    )
    if (-not $FilePath) {
        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).png")
        Write-Warning -Message "New-ImageQRCode - No file path specified, saving to $FilePath"
    }
    try {
        [ImagePlayground.QrCode]::Generate($Content, $FilePath, $false)

        if ($Show) {
            Invoke-Item -LiteralPath $FilePath
        }
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "New-ImageQRCodeWiFi - Error creating image $($_.Exception.Message)"
        }
    }
}
function New-ImageQRCodeWiFi {
    <#
    .SYNOPSIS
    Creates QR code for WiFi connection
 
    .DESCRIPTION
    Creates QR code for WiFi connection
 
    .PARAMETER SSID
    SSID of the WiFi network
 
    .PARAMETER Password
    Password of the WiFi network
 
    .PARAMETER FilePath
    File path to where the image with QR code should be saved.
 
    .PARAMETER Show
    Opens the image in default image viewer after saving
 
    .EXAMPLE
    New-ImageQRCodeWiFi -SSID 'Evotec' -Password 'Evotec' -FilePath "$PSScriptRoot\Samples\QRCodeWiFi.png"
 
    .NOTES
    General notes
    #>

    [alias('New-QRCodeWiFi')]
    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $SSID,
        [Parameter(Mandatory)][string] $Password,
        [Parameter(Mandatory)][string] $FilePath,
        [switch] $Show
    )
    if (-not $FilePath) {
        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).png")
        Write-Warning -Message "New-ImageQRCodeWiFi - No file path specified, saving to $FilePath"
    }
    try {
        [ImagePlayground.QrCode]::GenerateWiFi($ssid, $password, $FilePath, $false)

        if ($Show) {
            Invoke-Item -LiteralPath $FilePath
        }
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "New-ImageQRCodeWiFi - Error creating image $($_.Exception.Message)"
        }
    }
}
function New-ImageQRContact {
    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $FilePath,
        [QRCoder.PayloadGenerator+ContactData+ContactOutputType] $outputType = [QRCoder.PayloadGenerator+ContactData+ContactOutputType]::VCard4,
        [string] $Firstname,
        [string] $Lastname,
        [string] $Nickname ,
        [string] $Phone ,
        [string] $MobilePhone ,
        [string] $WorkPhone ,
        [string] $Email ,
        [DateTime] $Birthday ,
        [string] $Website ,
        [string] $Street ,
        [string] $HouseNumber ,
        [string] $City ,
        [string] $ZipCode ,
        [string] $Country ,
        [string] $Note ,
        [string] $StateRegion ,
        [ValidateSet('Default', 'Reversed')][string] $AddressOrder = 'Default',
        [string] $Org ,
        [string] $OrgTitle,
        [switch] $Show
    )

    if (-not $FilePath) {
        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).png")
        Write-Warning -Message "New-ImageQRContact - No file path specified, saving to $FilePath"
    }
    try {
        [ImagePlayground.QrCode]::GenerateContact(
            $filePath, $outputType, $firstname,
            $lastname, $nickname, $phone,
            $mobilePhone, $workPhone, $email,
            $birthday, $website, $street, $houseNumber,
            $city, $zipCode, $country, $note, $stateRegion, $addressOrder,
            $org, $orgTitle, $false
        )

        if ($Show) {
            Invoke-Item -LiteralPath $FilePath
        }
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "New-ImageQRContact - Error creating image $($_.Exception.Message)"
        }
    }
}
function Remove-ImageExif {
    <#
    .SYNOPSIS
    Removes EXIF data from image
 
    .DESCRIPTION
    Removes EXIF data from image.
 
    .PARAMETER FilePath
    File path to image to be processed for Exif Tag removal
 
    .PARAMETER FilePathOutput
    File path to where the image with removed Exif Tag should be saved. If not specified, the original image will be overwritten.
 
    .PARAMETER ExifTag
    Exif Tag to be removed from image
 
    .PARAMETER All
    Removes all Exif Tags from image
 
    .EXAMPLE
    Remove-ImageExif -FilePath "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg" -ExifTag [SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag]::ExposureTime
 
    .EXAMPLE
    Remove-ImageExif -FilePath "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg" -All
 
    .NOTES
    General notes
    #>

    [CmdletBinding(DefaultParameterSetName = 'RemoveExifTag')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Parameter(Mandatory, ParameterSetName = 'RemoveExifTag')]
        [string] $FilePath,
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Parameter(Mandatory, ParameterSetName = 'RemoveExifTag')]
        [string] $FilePathOutput,
        [Parameter(Mandatory, ParameterSetName = 'RemoveExifTag')]
        [SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag[]] $ExifTag,
        [Parameter(Mandatory, ParameterSetName = 'All')][switch] $All
    )
    $Image = Get-Image -FilePath $FilePath
    if ($All) {
        Write-Verbose "Remove-ImageExif: Removing all Exif tags"
        $Image.Metadata.ExifProfile.Values.Clear()
    } else {
        foreach ($Tag in $ExifTag) {
            Write-Verbose "Remove-ImageExif: Removing $Tag"
            $Image.Metadata.ExifProfile.RemoveValue($Tag)
        }
    }
    if ($FilePathOutput) {
        Save-Image -Image $Image -FilePath $FilePathOutput
    } else {
        Save-Image -Image $Image -FilePath $FilePath
    }
}
function Resize-Image {
    <#
    .SYNOPSIS
    Resizes the image to given width and height or percentage.
 
    .DESCRIPTION
    Resizes the image to given width and height or percentage.
    By default it will respect aspect ratio. If you want to ignore it use -DontRespectAspectRatio switch.
    This means you can only provide Width and the height will be automatically calculated or vice versa.
    You can also use percentage to resize the image maintaining the aspect ratio.
 
    .PARAMETER FilePath
    File path to the image you want to resize
 
    .PARAMETER OutputPath
    File path to the output image that will be created
 
    .PARAMETER Width
    New width of the image
 
    .PARAMETER Height
    New height of the image
 
    .PARAMETER Percentage
    Percentage of the image to resize
 
    .PARAMETER DontRespectAspectRatio
    If you want to ignore aspect ratio use this switch. It only affects Width and Height parameters that are used separately.
 
    .EXAMPLE
    Resize-Image -FilePath $PSScriptRoot\Samples\LogoEvotec.png -OutputPath $PSScriptRoot\Output\LogoEvotecResize.png -Width 100 -Height 100
 
    .EXAMPLE
    Resize-Image -FilePath $PSScriptRoot\Samples\LogoEvotec.png -OutputPath $PSScriptRoot\Output\LogoEvotecResizeMaintainAspectRatio.png -Width 300
 
    .EXAMPLE
    Resize-Image -FilePath $PSScriptRoot\Samples\LogoEvotec.png -OutputPath $PSScriptRoot\Output\LogoEvotecResizePercent.png -Percentage 200
 
    .NOTES
    General notes
    #>

    [cmdletBinding(DefaultParameterSetName = 'HeightWidth')]
    param(
        [parameter(ParameterSetName = 'Percentage')]
        [parameter(ParameterSetName = 'HeightWidth')]
        [parameter(Mandatory)][string] $FilePath,

        [parameter(ParameterSetName = 'Percentage')]
        [parameter(ParameterSetName = 'HeightWidth')]
        [parameter(Mandatory)][string] $OutputPath,

        [parameter(ParameterSetName = 'HeightWidth')][int] $Width,
        [parameter(ParameterSetName = 'HeightWidth')][int] $Height,
        [parameter(ParameterSetName = 'Percentage')][int] $Percentage,
        [parameter(ParameterSetName = 'HeightWidth')][switch] $DontRespectAspectRatio
    )
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        if ($Percentage) {
            [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $Percentage)
        } else {
            if ($DontRespectAspectRatio) {
                if ($PSBoundParameters.ContainsKey('Width') -and $PSBoundParameters.ContainsKey('Height')) {
                    [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $Width, $Height)
                } elseif ($PSBoundParameters.ContainsKey('Width')) {
                    [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $Width, $null, $false)
                } elseif ($PSBoundParameters.ContainsKey('Height')) {
                    [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $null, $Height, $false)
                } else {
                    Write-Warning -Message "Resize-Image - Please specify Width or Height or Percentage."
                }
            } else {
                if ($PSBoundParameters.ContainsKey('Width') -and $PSBoundParameters.ContainsKey('Height')) {
                    [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $Width, $Height)
                } elseif ($PSBoundParameters.ContainsKey('Width')) {
                    [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $Width, $null)
                } elseif ($PSBoundParameters.ContainsKey('Height')) {
                    [ImagePlayground.ImageHelper]::Resize($FilePath, $OutputPath, $null, $Height)
                } else {
                    Write-Warning -Message "Resize-Image - Please specify Width or Height or Percentage."
                }
            }
        }
    } else {
        Write-Warning -Message "Resize-Image - File $FilePath not found. Please check the path."
    }
}
function Save-Image {
    <#
    .SYNOPSIS
    Saves image that was open for processing
 
    .DESCRIPTION
    Saves image that was open for processing
 
    .PARAMETER Image
    Image object to be saved
 
    .PARAMETER FilePath
    File path to where the image should be saved. If not specified, the original image will be overwritten.
 
    .PARAMETER Open
    Opens the image in default image viewer after saving
 
    .EXAMPLE
    $Image = Get-Image -FilePath $PSScriptRoot\Samples\LogoEvotec.png
    $Image.BlackWhite()
    $Image.BackgroundColor("Red")
    Save-Image -Image $Image -Open -FilePath $PSScriptRoot\Output\LogoEvotecChanged.png
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [parameter(Mandatory)][ImagePlayground.Image] $Image,
        [string] $FilePath,
        [switch] $Open
    )
    if ($FilePath) {
        $Image.Save($FilePath, $Open.IsPresent)
    } else {
        $Image.Save($Open.IsPresent)
    }
}
function Set-ImageExif {
    <#
    .SYNOPSIS
    Sets EXIF tag to specific value
 
    .DESCRIPTION
    Sets EXIF tag to specific value
 
    .PARAMETER FilePath
    File path to image to be processed for Exif Tag manipulation. If FilePathOutput is not specified, the image will be overwritten.
 
    .PARAMETER FilePathOutput
    File path to output image. If not specified, the image will be overwritten.
 
    .PARAMETER ExifTag
    Exif Tag to be set
 
    .PARAMETER Value
    Value to be set
 
    .EXAMPLE
    $setImageExifSplat = @{
        FilePath = "C:\Users\przemyslaw.klys\Downloads\IMG_4644.jpeg"
        ExifTag = ([SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag]::DateTimeOriginal)
        Value = ([DateTime]::Now).ToString("yyyy:MM:dd HH:mm:ss")
        FilePathOutput = "$PSScriptRoot\Output\IMG_4644.jpeg"
    }
 
    Set-ImageExif @setImageExifSplat
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $FilePath,
        [string] $FilePathOutput,
        [Parameter(Mandatory)][SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag] $ExifTag,
        [Parameter(Mandatory)] $Value
    )
    if (-not (Test-Path $FilePath)) {
        Write-Warning -Message "Set-ImageExif - File not found: $FilePath"
        return
    }
    $Image = Get-Image -FilePath $FilePath
    # void SetValue[TValueType](SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag[TValueType] tag, TValueType value)
    $Image.Metadata.ExifProfile.SetValue($ExifTag, $Value)
    if ($FilePathOutput) {
        Save-Image -Image $Image -FilePath $FilePathOutput
    } else {
        Save-Image -Image $Image -FilePath $FilePath
    }
}


if ($PSVersionTable.PSEdition -eq 'Desktop' -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release -lt 461808) {
    Write-Warning "This module requires .NET Framework 4.7.2 or later."; return 
} 

# Export functions and aliases as required
Export-ModuleMember -Function @('ConvertTo-Image', 'Get-Image', 'Get-ImageBarCode', 'Get-ImageExif', 'Get-ImageQRCode', 'Merge-Image', 'New-ImageBarCode', 'New-ImageChart', 'New-ImageChartBar', 'New-ImageChartBarOptions', 'New-ImageChartLine', 'New-ImageChartPie', 'New-ImageChartRadial', 'New-ImageQRCode', 'New-ImageQRCodeWiFi', 'New-ImageQRContact', 'Remove-ImageExif', 'Resize-Image', 'Save-Image', 'Set-ImageExif') -Alias @('New-QRCode', 'New-QRCodeWiFi')