P3 GUIIn my previous article Pictures inventory and shown on an HTML page with Google Maps information I described all the steps to inventory all the pictures in a given folder and make an HTML report. What I missed was a GUI that shows the picture that is being processed. And that gap has been filled with this new script.

Under the hood, the same technique is used as in the script PictureDetails_v02.ps1. The only part that has been added is the GUI. For the GUI a PowerShell form is used. The only issue I had was that the pictures are not shown in the correct rotation. So you had to turn your head to see the pictures properly. Something like this with an example picture: 

P1 No Rotation

See the code below for an example:

# =============================================================================================================================================
# Demonstratie the rotation (or not) from the pictures in a picturebox.
# The pictures are downloaded from:   https://www.galloway.me.uk/2012/01/uiimageorientation-exif-orientation-sample-images/
# =============================================================================================================================================
   
  Clear-Host

# =============================================================================================================================================
# Add assemblies.
# =============================================================================================================================================

  Add-Type -AssemblyName System.Web                | Out-Null
  Add-Type -AssemblyName System.Drawing            | Out-Null

# =============================================================================================================================================
# Add SAPIENTypes.ProgressBarOverlay
# =============================================================================================================================================

Try
 {
  [ProgressBarOverlay] | Out-Null
 }
  Catch
 {
  if ($PSVersionTable.PSVersion.Major -ge 7)
   {
    $Assemblies = 'System.Windows.Forms', 'System.Drawing', 'System.Drawing.Primitives', 'System.ComponentModel.Primitives', 'System.Drawing.Common', 'System.Runtime'
   }
    else
   {
    $Assemblies = 'System.Windows.Forms', 'System.Drawing'  
   }
Add-Type -ReferencedAssemblies $Assemblies -TypeDefinition @"
using System;
using System.Windows.Forms;
using System.Drawing;
        namespace SAPIENTypes
        {
    public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
        {
                public ProgressBarOverlay() : base() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); }
            protected override void WndProc(ref Message m)
            { 
                base.WndProc(ref m);
                if (m.Msg == 0x000F)// WM_PAINT
                {
                    if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                        {
                            using (Graphics g = this.CreateGraphics())
                            {
                                using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                {
                                    stringFormat.Alignment = StringAlignment.Center;
                                    stringFormat.LineAlignment = StringAlignment.Center;
                                    if (!string.IsNullOrEmpty(this.Text))
                                        g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    else
                                    {
                                        int percent = (int)(((double)Value / (double)Maximum) * 100);
                                        g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    }
                                }
                            }
                        }
                }
            }
              
                public string TextOverlay
                {
                    get
                    {
                        return base.Text;
                    }
                    set
                    {
                        base.Text = value;
                        Invalidate();
                    }
                }
        }
        }
"@ -IgnoreWarnings | Out-Null
  }

# =============================================================================================================================================
# Define the variables
# =============================================================================================================================================

  $PicturesDir                                                  = $(Split-Path $MyInvocation.MyCommand.Definition) + "\EXIF_Orientation_Samples"

# =============================================================================================================================================
# Define the form
# =============================================================================================================================================

[System.Windows.Forms.Application]::EnableVisualStyles()
$frmDemoPictureBox                                              = New-Object 'System.Windows.Forms.Form'
$btnRun                                                         = New-Object 'System.Windows.Forms.Button'
$btnCancel                                                      = New-Object 'System.Windows.Forms.Button'
$pgbOverlay                                                     = New-Object 'SAPIENTypes.ProgressBarOverlay'
$lblProcessing                                                  = New-Object 'System.Windows.Forms.Label'
$pbPicturePreview                                               = New-Object 'System.Windows.Forms.PictureBox'

$frmDemoPictureBox.SuspendLayout()
#
# frmDemoPictureBox
#
$frmDemoPictureBox.Controls.Add($btnRun)
$frmDemoPictureBox.Controls.Add($btnCancel)
$frmDemoPictureBox.Controls.Add($pgbOverlay)
$frmDemoPictureBox.Controls.Add($lblProcessing)
$frmDemoPictureBox.Controls.Add($pbPicturePreview)
$frmDemoPictureBox.AutoScaleDimensions                          = New-Object System.Drawing.SizeF(6, 13)
$frmDemoPictureBox.AutoScaleMode                                = 'Font'
$frmDemoPictureBox.ClientSize                                   = New-Object System.Drawing.Size(784, 575)
$frmDemoPictureBox.Name                                         = 'frmDemoPictureBox'
$frmDemoPictureBox.StartPosition                                = 'CenterScreen'
$frmDemoPictureBox.Text                                         = 'Form to demonstrate a picture box (no rotation)'
#
# btnRun
#
$btnRun.Location                                                = New-Object System.Drawing.Point(615, 505)
$btnRun.Name                                                    = 'btnRun'
$btnRun.Size                                                    = New-Object System.Drawing.Size(75, 50)
$btnRun.TabIndex                                                = 8
$btnRun.Text                                                    = 'Run'
$btnRun.UseVisualStyleBackColor                                 = $True
$btnRun.add_Click({
                  $Pictures                = Get-ChildItem -Path $PicturesDir
                  $valCounter              = 1
                  $TotalPictures           = $Pictures.Count
                  ForEach($Picture in $Pictures)
                   {
                    $pgbOverlay.Value                 = [int]$valCounter / $TotalPictures * 100
                    $pgbOverlay.Update()
                    $lblProcessing.Text               = "Processing file: $($Picture.Name) (file $valCounter of $TotalPictures files)" 
                    $pbPicturePreview.Image           = [System.Drawing.Bitmap]$($Picture.FullName)
                    $pbPicturePreview.Update()
                    Sleep -Seconds 5
                    $valCounter++
                   }

                 })
#
# btnCancel
#
$btnCancel.Location                                             = New-Object System.Drawing.Point(696, 505)
$btnCancel.Name                                                 = 'btnCancel'
$btnCancel.Size                                                 = New-Object System.Drawing.Size(75, 50)
$btnCancel.TabIndex                                             = 7
$btnCancel.Text                                                 = 'Cancel'
$btnCancel.UseVisualStyleBackColor                              = $True
$btnCancel.add_Click({    
                      $frmDemoPictureBox.Close()
                      $frmDemoPictureBox.Dispose
                     })
#
# pgbOverlay
#
$pgbOverlay.Location                                            = New-Object System.Drawing.Point(12, 505)
$pgbOverlay.Name                                                = 'pgbOverlay'
$pgbOverlay.Size                                                = New-Object System.Drawing.Size(516, 50)
$pgbOverlay.Style                                               = 'Continuous'
$pgbOverlay.TabIndex                                            = 6
#
# lblProcessing
#
$lblProcessing.AutoSize                                         = $True
$lblProcessing.Location                                         = New-Object System.Drawing.Point(15, 475)
$lblProcessing.Name                                             = 'lblProcessing'
$lblProcessing.Size                                             = New-Object System.Drawing.Size(210, 13)
$lblProcessing.TabIndex                                         = 5
#
# pbPicturePreview
#
$pbPicturePreview.Location                                      = New-Object System.Drawing.Point(15, 25)
$pbPicturePreview.Name                                          = 'pbPicturePreview'
$pbPicturePreview.Size                                          = New-Object System.Drawing.Size(757, 437)
$pbPicturePreview.SizeMode                                      = 'Zoom'
$pbPicturePreview.TabIndex                                      = 0
$pbPicturePreview.TabStop                                       = $False



[void]$frmDemoPictureBox.ShowDialog()

The solution was rather simple. In the pictures' EXIF information in &H0112 the first byte represents the orientation. See PropertyTagOrientation for more information. Also, the article Getting correct Image rotation [duplicate] was a great help for me.

P2 Rotation

With that knowledge I created the following demo script:

# =============================================================================================================================================
# Demonstratie the rotation (or not) from the pictures in a picturebox.
# The pictures are downloaded from:   https://www.galloway.me.uk/2012/01/uiimageorientation-exif-orientation-sample-images/
# =============================================================================================================================================


# =============================================================================================================================================
# Function block
# =============================================================================================================================================
 
  Function Get-Orientation
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-May-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-Orientation
    =============================================================================================================================================
    .SYNOPSIS

    This function reads the orientation of a picture.
    It reads the first digit from ID &H0112

    #>

    Param
     (
      [String] $SourceFileName
     )

    $img                 = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $SourceFileName
    Try
     {
      $IMGOrientation      = ($img.PropertyItems | Where {($_.ID -eq 274)}).Value[0]
     }
      Catch
     {
      $IMGOrientation      = 0
     }

    Return $IMGOrientation 

  } 

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

  Clear-Host

# =============================================================================================================================================
# Add assemblies.
# =============================================================================================================================================

  Add-Type -AssemblyName System.Web                | Out-Null
  Add-Type -AssemblyName System.Drawing            | Out-Null
  Add-Type -AssemblyName System.Windows.Forms      | Out-Null

# =============================================================================================================================================
# Add SAPIENTypes.ProgressBarOverlay
# =============================================================================================================================================

Try
 {
  [ProgressBarOverlay] | Out-Null
 }
  Catch
 {
  if ($PSVersionTable.PSVersion.Major -ge 7)
   {
    $Assemblies = 'System.Windows.Forms', 'System.Drawing', 'System.Drawing.Primitives', 'System.ComponentModel.Primitives', 'System.Drawing.Common', 'System.Runtime'
   }
    else
   {
    $Assemblies = 'System.Windows.Forms', 'System.Drawing'  
   }
Add-Type -ReferencedAssemblies $Assemblies -TypeDefinition @"
using System;
using System.Windows.Forms;
using System.Drawing;
        namespace SAPIENTypes
        {
    public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
        {
                public ProgressBarOverlay() : base() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); }
            protected override void WndProc(ref Message m)
            { 
                base.WndProc(ref m);
                if (m.Msg == 0x000F)// WM_PAINT
                {
                    if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                        {
                            using (Graphics g = this.CreateGraphics())
                            {
                                using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                {
                                    stringFormat.Alignment = StringAlignment.Center;
                                    stringFormat.LineAlignment = StringAlignment.Center;
                                    if (!string.IsNullOrEmpty(this.Text))
                                        g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    else
                                    {
                                        int percent = (int)(((double)Value / (double)Maximum) * 100);
                                        g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    }
                                }
                            }
                        }
                }
            }
              
                public string TextOverlay
                {
                    get
                    {
                        return base.Text;
                    }
                    set
                    {
                        base.Text = value;
                        Invalidate();
                    }
                }
        }
        }
"@ -IgnoreWarnings | Out-Null
  }

# =============================================================================================================================================
# Define the variables
# =============================================================================================================================================

  $PicturesDir                                                  = $(Split-Path $MyInvocation.MyCommand.Definition) + "\EXIF_Orientation_Samples"

# =============================================================================================================================================
# Define the form
# =============================================================================================================================================

[System.Windows.Forms.Application]::EnableVisualStyles()
$frmDemoPictureBox                                              = New-Object 'System.Windows.Forms.Form'
$btnRun                                                         = New-Object 'System.Windows.Forms.Button'
$btnCancel                                                      = New-Object 'System.Windows.Forms.Button'
$pgbOverlay                                                     = New-Object 'SAPIENTypes.ProgressBarOverlay'
$lblProcessing                                                  = New-Object 'System.Windows.Forms.Label'
$pbPicturePreview                                               = New-Object 'System.Windows.Forms.PictureBox'

$frmDemoPictureBox.SuspendLayout()
#
# frmDemoPictureBox
#
$frmDemoPictureBox.Controls.Add($btnRun)
$frmDemoPictureBox.Controls.Add($btnCancel)
$frmDemoPictureBox.Controls.Add($pgbOverlay)
$frmDemoPictureBox.Controls.Add($lblProcessing)
$frmDemoPictureBox.Controls.Add($pbPicturePreview)
$frmDemoPictureBox.AutoScaleDimensions                          = New-Object System.Drawing.SizeF(6, 13)
$frmDemoPictureBox.AutoScaleMode                                = 'Font'
$frmDemoPictureBox.ClientSize                                   = New-Object System.Drawing.Size(784, 575)
$frmDemoPictureBox.Name                                         = 'frmDemoPictureBox'
$frmDemoPictureBox.StartPosition                                = 'CenterScreen'
$frmDemoPictureBox.Text                                         = 'Form to demonstrate a picture box (with rotation)'
#
# btnRun
#
$btnRun.Location                                                = New-Object System.Drawing.Point(615, 505)
$btnRun.Name                                                    = 'btnRun'
$btnRun.Size                                                    = New-Object System.Drawing.Size(75, 50)
$btnRun.TabIndex                                                = 8
$btnRun.Text                                                    = 'Run'
$btnRun.UseVisualStyleBackColor                                 = $True
$btnRun.add_Click({
                  $Pictures                = Get-ChildItem -Path $PicturesDir
                  $valCounter              = 1
                  $TotalPictures           = $Pictures.Count
                  ForEach($Picture in $Pictures)
                   {
                    $pgbOverlay.Value                 = [int]$valCounter / $TotalPictures * 100
                    $pgbOverlay.Update()
                    $lblProcessing.Text               = "Processing file: $($Picture.Name) (file $valCounter of $TotalPictures files)"
                    $Orientation                      = Get-Orientation -SourceFileName $($Picture.FullName) 
                    $pbPicturePreview.Image           = [System.Drawing.Bitmap]$($Picture.FullName)
                    if($Orientation -eq 2) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipX)}
                    if($Orientation -eq 3) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipXY)}
                    if($Orientation -eq 4) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipY)}
                    if($Orientation -eq 5) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipX)}
                    if($Orientation -eq 6) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipNone)}
                    if($Orientation -eq 7) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipY)}
                    if($Orientation -eq 8) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipXY)}
                    $pbPicturePreview.Update()
                    Sleep -Seconds 5
                    $valCounter++
                   }

                 })
#
# btnCancel
#
$btnCancel.Location                                             = New-Object System.Drawing.Point(696, 505)
$btnCancel.Name                                                 = 'btnCancel'
$btnCancel.Size                                                 = New-Object System.Drawing.Size(75, 50)
$btnCancel.TabIndex                                             = 7
$btnCancel.Text                                                 = 'Cancel'
$btnCancel.UseVisualStyleBackColor                              = $True
$btnCancel.add_Click({    
                      $frmDemoPictureBox.Close()
                      $frmDemoPictureBox.Dispose
                     })
#
# pgbOverlay
#
$pgbOverlay.Location                                            = New-Object System.Drawing.Point(12, 505)
$pgbOverlay.Name                                                = 'pgbOverlay'
$pgbOverlay.Size                                                = New-Object System.Drawing.Size(516, 50)
$pgbOverlay.Style                                               = 'Continuous'
$pgbOverlay.TabIndex                                            = 6
#
# lblProcessing
#
$lblProcessing.AutoSize                                         = $True
$lblProcessing.Location                                         = New-Object System.Drawing.Point(15, 475)
$lblProcessing.Name                                             = 'lblProcessing'
$lblProcessing.Size                                             = New-Object System.Drawing.Size(210, 13)
$lblProcessing.TabIndex                                         = 5
#
# pbPicturePreview
#
$pbPicturePreview.Location                                      = New-Object System.Drawing.Point(15, 25)
$pbPicturePreview.Name                                          = 'pbPicturePreview'
$pbPicturePreview.Size                                          = New-Object System.Drawing.Size(757, 437)
$pbPicturePreview.SizeMode                                      = 'Zoom'
$pbPicturePreview.TabIndex                                      = 0
$pbPicturePreview.TabStop                                       = $False



[void]$frmDemoPictureBox.ShowDialog()

And the orientation part has been implemented in the form. See the script below.
It is still possible to use the silent option. So you are not forced to use the GUI. When using the GUI the settings are stored in the registry under HKCU:Software\VroomSoft\PictureDetails. So you only have to enter the GoogleAPIKey once.

Get-Help PictureDetailsWithForms_v01.ps1 -Detailed


NAME
    C:\PictureDetailsWithForms_v01\PictureDetailsWithForms_v01.ps1
    
SYNOPSIS
    Finds all the jpg files in a folder and puts the information in a html file. Also the location where the picture 
    has been taken is shown
    in Google Maps if the Google Maps API key is provided.
    
    
SYNTAX
    C:\PictureDetailsWithForms_v01\PictureDetailsWithForms_v01.ps1 [-GoogleAPIKey <String>] [-LogPath <String>] 
    [-Directory <String>] [-DetailedLogging] [-IncludeSubFolders] [-ShowOnlyUniquePictures] [-Silent] [-SaveAsPDF] 
    [-ClearUserSettings] [-NoUserSettings] [<CommonParameters>]
    
    
DESCRIPTION
    Finds all the jpg files in a folder and puts the information in a html file. Also the location where the picture 
    has been taken is shown
    in Google Maps if the Google Maps API key is provided.
    

PARAMETERS
    -GoogleAPIKey <String>
        
    -LogPath <String>
        
    -Directory <String>
        
    -DetailedLogging [<SwitchParameter>]
        
    -IncludeSubFolders [<SwitchParameter>]
        
    -ShowOnlyUniquePictures [<SwitchParameter>]
        
    -Silent [<SwitchParameter>]
        
    -SaveAsPDF [<SwitchParameter>]
        
    -ClearUserSettings [<SwitchParameter>]
        
    -NoUserSettings [<SwitchParameter>]
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see 
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216). 
    
    -------------------------- EXAMPLE 1 --------------------------
    
    PS C:\>Run an inventory on all the pictures in the current directory
    
    ."PictureDetailsWithForms_v01.ps1"
    
    
    
    
    -------------------------- EXAMPLE 2 --------------------------
    
    PS C:\>Run an inventory on all the pictures in the current directory and log in C:\TMP
    
    ."PictureDetailsWithForms_v01.ps1" -LogPath c:\tmp
    
    
    
    
    -------------------------- EXAMPLE 3 --------------------------
    
    PS C:\>Run an inventory on all the pictures in the current directory and detailed logging in C:\TMP
    
    ."PictureDetailsWithForms_v01.ps1" -LogPath c:\tmp -DetailedLogging
    
    
    
    
    -------------------------- EXAMPLE 4 --------------------------
    
    PS C:\>Run an inventory on all the pictures in the current directory and use the Google API Key 
    1234567890-0987654321. Include the subfolders.
    
    ."PictureDetailsWithForms_v01.ps1" -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders
    
    
    
    
    -------------------------- EXAMPLE 5 --------------------------
    
    PS C:\>Run an inventory on all the pictures in the current directory and use the Google API Key 
    1234567890-0987654321. Include the subfolders.
    
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are 
    skipped.
    ."PictureDetailsWithForms_v01.ps1" -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -ShowOnlyUniquePictures
    
    
    
    
    -------------------------- EXAMPLE 6 --------------------------
    
    PS C:\>Run an inventory on all the pictures in C:\Pictures and use the Google API Key 1234567890-0987654321. 
    Include the subfolders.
    
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are 
    skipped.
    ."PictureDetailsWithForms_v01.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders 
    -ShowOnlyUniquePictures
    
    
    
    
    -------------------------- EXAMPLE 7 --------------------------
    
    PS C:\>Run an inventory on all the pictures in C:\Pictures and use the Google API Key 1234567890-0987654321. 
    Include the subfolders.
    
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are 
    skipped. Run all in Silent mode.
    ."PictureDetailsWithForms_v01.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders 
    -ShowOnlyUniquePictures -Silent
    
    
    
    
    -------------------------- EXAMPLE 8 --------------------------
    
    PS C:\>Clear all the stored settings before running the script.
    
    ."PictureDetailsWithForms_v01.ps1" -ClearUserSettings
    
    
    
    
    -------------------------- EXAMPLE 9 --------------------------
    
    PS C:\>Run an inventory on all the pictures in C:\Pictures and use the Google API Key 1234567890-0987654321. 
    Include the subfolders.
    
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are 
    skipped. Do not save the settings on exit.
    ."PictureDetailsWithForms_v01.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders 
    -ShowOnlyUniquePictures -NoUserSettings
    
    
    
    
REMARKS
    To see the examples, type: "get-help C:\PictureDetailsWithForms_v01\PictureDetailsWithForms_v01.ps1 -examples".
    For more information, type: "get-help C:\PictureDetailsWithForms_v01\PictureDetailsWithForms_v01.ps1 -detailed".
    For technical information, type: "get-help C:\PictureDetailsWithForms_v01\PictureDetailsWithForms_v01.ps1 -full".

 Finally, the script:

<#
.SYNOPSIS
    Finds all the jpg files in a folder and puts the information in a html file. Also the location where the picture has been taken is shown
    in Google Maps if the Google Maps API key is provided. 

.DESCRIPTION
    Finds all the jpg files in a folder and puts the information in a html file. Also the location where the picture has been taken is shown
    in Google Maps if the Google Maps API key is provided. 

.EXAMPLE
    Run an inventory on all the pictures in the current directory
    ."PictureDetailsWithForms_v01.ps1"

.EXAMPLE
    Run an inventory on all the pictures in the current directory and log in C:\TMP
    ."PictureDetailsWithForms_v01.ps1" -LogPath c:\tmp

.EXAMPLE
    Run an inventory on all the pictures in the current directory and detailed logging in C:\TMP
    ."PictureDetailsWithForms_v01.ps1" -LogPath c:\tmp -DetailedLogging

.EXAMPLE
    Run an inventory on all the pictures in the current directory and use the Google API Key 1234567890-0987654321. Include the subfolders.
    ."PictureDetailsWithForms_v01.ps1" -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders

.EXAMPLE
    Run an inventory on all the pictures in the current directory and use the Google API Key 1234567890-0987654321. Include the subfolders.
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are skipped.
    ."PictureDetailsWithForms_v01.ps1" -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -ShowOnlyUniquePictures

.EXAMPLE
    Run an inventory on all the pictures in C:\Pictures and use the Google API Key 1234567890-0987654321. Include the subfolders.
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are skipped.
    ."PictureDetailsWithForms_v01.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -ShowOnlyUniquePictures

.EXAMPLE
    Run an inventory on all the pictures in C:\Pictures and use the Google API Key 1234567890-0987654321. Include the subfolders.
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are skipped. Run all in Silent mode.
    ."PictureDetailsWithForms_v01.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -ShowOnlyUniquePictures -Silent

.EXAMPLE
    Clear all the stored settings before running the script. 
    ."PictureDetailsWithForms_v01.ps1" -ClearUserSettings

.EXAMPLE
    Run an inventory on all the pictures in C:\Pictures and use the Google API Key 1234567890-0987654321. Include the subfolders.
    Show only the unique pictures: the filename discovered only once, all the other files with the same filename are skipped. Do not save the settings on exit.
    ."PictureDetailsWithForms_v01.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -ShowOnlyUniquePictures -NoUserSettings

.NOTES
    Author:  Willem-Jan Vroom
    Website: https://www.vroom.cc/
    Twitter: @TheStingPilot

v0.1:
   * Initial version.

#>

[CmdletBinding(DefaultParameterSetName = 'Default')]

Param
  (
   [Parameter(HelpMessage='Specify the Google API key.')]
   [Parameter(Mandatory=$False,  ParameterSetName='Default')]
   [String]   $GoogleAPIKey = "",

   [Parameter(HelpMessage='Specify the log path.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [String]   $LogPath = "",

   [Parameter(HelpMessage='Specify the directory that contains all images.')]
   [Parameter(Mandatory=$false,  ParameterSetName = 'Default')]
   [String]   $Directory = "",

   [Parameter(HelpMessage='Enable detailed logging to a log file.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $DetailedLogging,

   [Parameter(HelpMessage='Include subfolders.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $IncludeSubFolders,

   [Parameter(HelpMessage='Show only the unique pictures.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $ShowOnlyUniquePictures,

   [Parameter(HelpMessage='Run the script silently, without user intervention.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $Silent,

   [Parameter(HelpMessage='Save the output as a PDF File.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $SaveAsPDF,

   [Parameter(HelpMessage='Clear the user settings from the registry.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $ClearUserSettings,

   [Parameter(HelpMessage='Do not use the saved user settings and do not save the user settings on exit.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $NoUserSettings

  )

# =============================================================================================================================================
# Function block
# =============================================================================================================================================

  Function WorkingInternetConnection
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       09-July-2020
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Check-Internet
    =============================================================================================================================================
    .SYNOPSIS

    Check for a working internet connection.

    #>
    Try
     {
      Invoke-WebRequest -Uri "https://www.google.com" -ErrorAction SilentlyContinue
      Add-EntryToLogFile -Entry "The website google.com can be reached. Thus there is a working internet connection."
      Return $True
     }
      Catch
     {
      Add-EntryToLogFile -Entry "The website google.com cannot be reached. Thus there is no working internet connection."
      Return $False
     }
   }

  Function Display-MessageBox
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       24-May-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Display-MessageBox
    =============================================================================================================================================
    .SYNOPSIS

    This function displays a message box.

    #>

    param
     (
      [String] $Text,
      [Switch] $GUI,
      [Switch] $Error
     )
         
    if($GUI)
     {
      if($Error)
       {
        [System.Windows.MessageBox]::Show($Text,"Error","OK","Error")
       }
        else
       {
        [System.Windows.MessageBox]::Show($Text,"Information","OK","Asterisk")
       }
     }
      else
     {
      Write-Host "$Text`n"
     }
   }

  Function Create-Folder
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-August-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Create-Folder
    =============================================================================================================================================
    .SYNOPSIS

    This function creates the given folder.
    The function returns two results:
     1. A message.
     2. True or false (success or failure)

    #>
    
    param
     (
      [String] $FolderName
     )

   $bolResult     = $True
   $ResultMessage = ""

   if(-not(Test-Path $('FileSystem::' + $FolderName)))
    {
     New-Item -Path $FolderName -ItemType Directory | Out-Null
     Sleep 1
     if(test-path $('FileSystem::' + $FolderName))
      {
       $ResultMessage = "The folder '$FolderName' has been created."
      }
       else
      {
       $ResultMessage = "Something went wrong while creating the folder '$FolderName'. "
       $bolResult     = $false
      }
    }
     else
    {
     $ResultMessage = "The folder $FolderName already exists."
    }

    Return $ResultMessage,$bolResult 

   }

  Function Add-EntryToLogFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-May-2020
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-EntryToLogFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds a line to a log file

    #>

    Param
     (
      [string] $Entry
     )
      
     Write-Verbose $Entry
     if($Global:gblDetailedLogging)
      {
       $Timestamp = (Get-Date -UFormat "%a %e %b %Y %X").ToString()
       Add-Content $Global:gblLogFile -Value $($Timestamp + " " + $Entry) -Force -ErrorAction SilentlyContinue
      }
   }

  Function Set-Registry
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-February-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Set-Registry
    =============================================================================================================================================
    .SYNOPSIS

    This function sets a registry key
    
    #>

    param
     (
      [Parameter(Mandatory=$True) ] [String]$RegistryKey,
      [Parameter(Mandatory=$True) ] [String]$ValueName,
      [Parameter(Mandatory=$True) ] [String]$Value,
      [Parameter(Mandatory=$False)] [Switch]$Boolean,
      [Parameter(Mandatory=$False)] [Switch]$IsArray
     )

     # Check if the $Registrykey exists

     if(-not(test-path($RegistryKey)))
      {
       $Key    = $RegistryKey.Split(":")[1]
       $NewKey = $($RegistryKey.Split(":")[0]) + ":"
       foreach($Item in $($Key.Split("\")))
        {
         $NewKey += "\$Item"
         if(-not(test-path($NewKey)))
          {
           New-Item $NewKey -Force | Out-Null
           Add-EntryToLogFile "Created the registry key: $NewKey."
          }
        }

      }
     
     if($Boolean)
      {
       if($Value)
        { 
         New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType DWord -Value 1 -Force | Out-Null
         Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):"
         Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"
         Add-EntryToLogFile -Entry "          Type:      REG_DWORD"
         Add-EntryToLogFile -Entry "          Value:     1" 
        }
         else
        {
         New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType DWord -Value 0 -Force | Out-Null
         Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):"
         Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"
         Add-EntryToLogFile -Entry "          Type:      REG_DWORD"
         Add-EntryToLogFile -Entry "          Value:     0" 
        }
      }
       elseif($IsArray)
      {
       New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType String -Value $($Value -join ",") -Force | Out-Null
       Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):"
       Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"
       Add-EntryToLogFile -Entry "          Type:      REG_SZ"
       Add-EntryToLogFile -Entry "          Value:     $($Value -join ",")" 
      }
       else
      {
       New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType String -Value $Value -Force | Out-Null
       Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):"
       Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"
       Add-EntryToLogFile -Entry "          Type:      REG_SZ"
       Add-EntryToLogFile -Entry "          Value:     $Value" 
      }
   }

  Function Read-Registry
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-February-2021 / Modified 14-May-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Read-Registry
    =============================================================================================================================================
    .SYNOPSIS

    This function reads a registry key and returns the value
    
    #>

    param
     (
      [Parameter(Mandatory=$True)] [String]$RegistryKey,
      [Parameter(Mandatory=$True)] [String]$ValueName
     )

    if(Test-Path ($RegistryKey))
     {
      $Value        = (Get-ItemProperty -Path $RegistryKey).$ValueName
      if(($Value -ne $null) -and ($Value.Length -ne 0))
       {
        Add-EntryToLogFile -Entry "The registrykey '$RegistryKey' with valuename '$ValueName' exists."
        $PropertyType = (Get-Item $RegistryKey).GetValueKind($ValueName)
        Add-EntryToLogFile -Entry "Hive:          $RegistryKey"
        Add-EntryToLogFile -Entry "  - ValueName: $ValueName"
        Add-EntryToLogFile -Entry "  - Type:      $PropertyType"
        Add-EntryToLogFile -Entry "  - Value:     $Value"
        if($PropertyType -eq "String")
         {
          Return $Value
         }
          elseif($PropertyType -eq "DWord")
         {
          if($Value -eq 1)
           {
            Return $True
           }
            else
           {
            Return $False
           }
         }
       }
     }
     Return [string]::Empty
   }

  Function ReadAndSetSettings
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-February-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     ReadAndSetSettings
    =============================================================================================================================================
    .SYNOPSIS

    This function reads or sets the settings in the registry.

    #>

    param
     (
      [Switch] $Set,
      [String] $RegistryKey
     )
  

    if($Set)
     {
      if(test-path ($RegistryKey))
       {
        Remove-Item -Path $RegistryKey -Force
       }
      if($Global:gblGoogleAPIKey)           {Set-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey"             -Value $Global:gblGoogleAPIKey}
      if($Global:gblDirectory)              {Set-Registry -RegistryKey $RegistryKey -ValueName "Directory"                -Value $Global:gblDirectory}
      if($Global:gblIncludeSubFolders)      {Set-Registry -Boolean -RegistryKey $RegistryKey -ValueName "SubFolders"      -Value $Global:gblIncludeSubFolders}
      if($Global:gblShowOnlyUniquePictures) {Set-Registry -Boolean -RegistryKey $RegistryKey -ValueName "UniquePictures"  -Value $Global:gblShowOnlyUniquePictures}
     }
   }

  Function Add-EntryToHTMLTable
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-August-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-EntryToHTMLTable
    =============================================================================================================================================
    .SYNOPSIS

    This function adds information to a HTML table for later use.

    #>

    Param
     (
      [String]  $Picture           = "",
      [String]  $PhotoData         = "",
      [String]  $GoogleMapsMap     = ""
     )

    $Record  = [ordered] @{"Picture"                           = "";
                           "Photo Data"                        = "";
                           "Google Maps Map"                   = ""
                           }

    $Record."Picture"                   = $Picture
    $Record."Photo Data"                = $PhotoData
    $Record."Google Maps Map"           = $GoogleMapsMap
 
    $objRecord                          = New-Object PSObject -Property $Record
    $Global:arrHTMLTable               += $objRecord

    Add-EntryToLogFile -Entry  "    Picture:           $Picture"
    Add-EntryToLogFile -Entry  "    Photo Data:        $PhotoData"
    Add-EntryToLogFile -Entry  "    Google Maps Map:   $GoogleMapsMap"
    Add-EntryToLogFile -Entry  ""
    Add-EntryToLogFile -Entry  "###############################################################################################################`n"
   }

  Function Get-PhotoMetaDataFromJPGFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-August-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-PhotoDataFromJPGFile
    =============================================================================================================================================
    .SYNOPSIS

    This function reads all the metadata from a JPG file
    This function is based on Get-FileMetaDataReturnObject.ps1 from the Microsoft Scripting Guys

    #>

   Param
     (
      [String] $FileName
     )

    $tmp        = Get-ChildItem $FileName 
    $pathname   = $tmp.DirectoryName 
    $Name       = $tmp.Name 
 
    $objShell   = New-Object -ComObject Shell.Application 
    $objFolder  = $objShell.namespace($pathname) 
    $objFile    = $objFolder.parsename($Name) 
    $Results    = New-Object PSOBJECT 

    for($a=0; $a -le 1024; $a++) 
    { 
        if($objFolder.getDetailsOf($objFolder, $a) -and $objFolder.getDetailsOf($objFile, $a))  
        { 
            $hash += @{$($objFolder.getDetailsOf($objFolder, $a)) = $($objFolder.getDetailsOf($objFile, $a))} 
            $Results | Add-Member $hash -Force 
        } 
    } 

    $ReturnString = ""
    ForEach ($Result in $Results -split ";")
     {
      $ReturnString += $Result + "<br>"
     }

    $hash.Clear() 
    $ReturnString = $ReturnString -replace "@{","" -replace "}",""
    Return $ReturnString
 
  }

  Function Get-Orientation
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-May-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-Orientation
    =============================================================================================================================================
    .SYNOPSIS

    This function reads the orientation of a picture.

    #>

    Param
     (
      [String] $SourceFileName
     )

    $img                 = New-Object -TypeName system.drawing.bitmap -ArgumentList $SourceFileName
    Try
     {
      $IMGOrientation      = ($img.PropertyItems | Where {($_.ID -eq 274)}).Value[0]
     }
      Catch
     {
      $IMGOrientation      = 0
     }

    Return $IMGOrientation 

  }  
  
  Function Get-GPSDetails
   {
 
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-July-2020
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-GPSDetails
    =============================================================================================================================================
    .SYNOPSIS

    This function reads the latitude and longitude data from a JPG file. More information can be found at:
    http://www.forensicexpedition.com/2017/08/03/imagemapper-a-powershell-metadata-and-geo-maping-tool-for-images/

    More information about the EXIF layout can be found at: https://exiftool.org/TagNames/GPS.html

    #>
 
    param
     (
      [int]    $ID,
      [String] $FileNameWithGPSDetails
     )

    $img                 = New-Object -TypeName system.drawing.bitmap -ArgumentList $FileNameWithGPSDetails
    $IMGPropertyItems    = $img.PropertyItems | Where {($_.ID -eq $ID)}
    [Double]$Deg         = 0
    [Double]$Min         = 0
    [Double]$Sec         = 0
    $GPSTable            = @()

    For($a=0;$a -le 20;$a++)
     {
     
      $GPSRecord  = [ordered] @{"Number"                   = "";
                                "Value"                    = "";
                                "Type"                     = "";
                                "Decimal value (Signed)"   = "";
                                "Decimal value (Unsigned)" = "";
                                "Remark"                   = ""
                                }
     
      Switch ($a)
       {
        "0"  {$Remark = "Deg1"; Break}
        "4"  {$Remark = "Deg2"; Break}
        "8"  {$Remark = "Min1"; Break}
        "12" {$Remark = "Min2"; Break}
        "16" {$Remark = "Sec1"; Break}
        "20" {$Remark = "Sec2"; Break}
        Default {$Remark = ""}
       }
     
      $GPSRecord."Number"                   = $a
      $GPSRecord."Value"                    = $IMGPropertyItems.Value[$a]
      $GPSRecord."Type"                     = $IMGPropertyItems.Value[$a].Gettype()
      $GPSRecord."Decimal value (Signed)"   = ([System.BitConverter]::ToInt32($img.GetPropertyItem($ID).Value, $a))
      $GPSRecord."Decimal value (Unsigned)" = ([System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, $a))
      $GPSRecord."Remark"                   = $Remark
      $objGPSRecord                         = New-Object PSObject -Property $GPSRecord
      $GPSTable                            += $objGPSRecord
    } 
                   
    [Double]$Deg1 = ([Decimal][System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, 0))
    [Double]$Deg2 = ([Decimal][System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, 4))
    [Double]$Deg = $Deg1 / $Deg2

    [Double]$Min1 = ([Decimal][System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, 8))
    [Double]$Min2 = ([Decimal][System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, 12))
    [Double]$Min  = $Min1 / $Min2

    [Double]$Sec1 = ([Decimal][System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, 16))
    [Double]$Sec2 = ([Decimal][System.BitConverter]::ToUInt32($img.GetPropertyItem($ID).Value, 20))
    [Double]$Sec = $Sec1 / $Sec2
    
    Add-EntryToLogFile -Entry ""
    Add-EntryToLogFile -Entry "     --- Overview of the EXIF GPS Data ---"
    Add-EntryToLogFile -Entry "     --- ID $ID of $(Split-Path -Leaf $FileNameWithGPSDetails)"
    ForEach ($object in $GPSTable)
     {
      if($object."Remark")
       {
        Add-EntryToLogFile -Entry "       Number:                     $($object."Number")"
        Add-EntryToLogFile -Entry "       Value:                      $($object."Value")"
        Add-EntryToLogFile -Entry "       Type:                       $($object."Type")"
        Add-EntryToLogFile -Entry "       Decimal value (Signed)    : $($object."Decimal value (Signed)")"
        Add-EntryToLogFile -Entry "       Decimal value (Unsigned)  : $($object."Decimal value (Unsigned)")"
        Add-EntryToLogFile -Entry "       Remark:                     $($object."Remark")"
        Add-EntryToLogFile -Entry "       ---------------------------------------------------------------------"
       }
     }

    Add-EntryToLogFile "         (Unsigned decimal value)  Deg1 / Deg2   ->   $Deg1 / $Deg2 = $Deg"
    Add-EntryToLogFile "         (Unsigned decimal value)  Min1 / Min2   ->   $Min1 / $Min2 = $Min"
    Add-EntryToLogFile "         (Unsigned decimal value)  Sec1 / Sec2   ->   $Sec1 / $Sec2 = $Sec"
        
    $GPSTable.Clear()
    
    Return [Double]$Deg, [Double]$Min, [Double]$Sec
 }


Function Get-EXIFDataFromJPGFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       06-July-2020
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-EXIFDataFromJPGFile
    =============================================================================================================================================
    .SYNOPSIS

    This function reads the latitude and longitude data from a JPG file.
    If no valid data is found, the return codes will be 1024 and 1024.
    This code is based on 
    http://www.forensicexpedition.com/2017/08/03/imagemapper-a-powershell-metadata-and-geo-maping-tool-for-images/

    More information about the EXIF layout can be found at: https://exiftool.org/TagNames/GPS.html

    #>

    Param
     (
      [String] $FileName
     )
    
    
    $img     = New-Object -TypeName system.drawing.bitmap -ArgumentList $FileName
    $Encode  = New-Object System.Text.ASCIIEncoding
    $GPSInfo = $true
    $GPSLat  = ""
    $GPSLon  = ""
      
    # =============================================================================================================================================
    # Try to get the latitude (N or S) from the image.
    # If not successfull then this information is not in the image
    # and quit with the numbers 1024 and 1024. 
    # =============================================================================================================================================

    Try
     {
      
      $LatNS = $Encode.GetString($img.GetPropertyItem(1).Value)
     }
      Catch
     {
      $GPSInfo = $False
     }
                
    If ($GPSInfo -eq $true)
     {
      $LonEW = $Encode.GetString($img.GetPropertyItem(3).Value)
      
      [Double]$LatDeg, [Double]$LatMin, [Double]$LatSec = Get-GPSDetails -FileNameWithGPSDetails $FileName -ID 2 
      [Double]$LonDeg, [Double]$LonMin, [Double]$LonSec = Get-GPSDetails -FileNameWithGPSDetails $FileName -ID 4

      $GPSLat = "$([int]$LatDeg)º $([int]$LatMin)' $($([double]$LatSec).ToString("##.######"))$([char]34) $LatNS"
      $GPSLon = "$([int]$LonDeg)º $([int]$LonMin)' $($([double]$LonSec).ToString("##.######"))$([char]34) $LonEW"

      Add-EntryToLogFile -Entry "    The picture $FileName has the following GPS coordinates:"
      Add-EntryToLogFile -Entry "       $GPSLat"        
      Add-EntryToLogFile -Entry "       $GPSLon"

    # =============================================================================================================================================
    # Convert the latitude and longitude to numbers that Google Maps recognizes.
    # =============================================================================================================================================

      $LatOrt = 0
      $LonOrt = 0

      If ($LatNS -eq 'S')
       {
        $LatOrt = "-"   
       }
      If ($LonEW -eq 'W')
       {
        $LonOrt = "-"
       }

      $LatDec = ($LatDeg + ($LatMin/60) + ($LatSec/3600))
      $LonDec = ($LonDeg + ($LonMin/60) + ($LonSec/3600))

      $LatOrt = $LatOrt + $LatDec
      $LonOrt = $LonOrt + $LonDec

    # =============================================================================================================================================
    # The numbers that where returned contained a decimal comma instead of a decimal point. 
    # So the en-US culture is forced to get the correct number notation.
    # =============================================================================================================================================
    
      $LatOrt = $LatOrt.ToString([cultureinfo]::GetCultureInfo('en-US'))
      $LonOrt = $LonOrt.ToString([cultureinfo]::GetCultureInfo('en-US'))

      Add-EntryToLogFile -Entry "    The picture $FileName has the following decimal coordinates:"
      Add-EntryToLogFile -Entry "      $LatOrt"
      Add-EntryToLogFile -Entry "      $LonOrt"
    }
     else
    {
     
   # =============================================================================================================================================
   # Ohoh... No GPS information in this picture.
   # =============================================================================================================================================
     
     Add-EntryToLogFile -Entry "    The picture $FileName does not contain GPS information."
     $LatOrt = "1024"
     $LonOrt = "1024"
    }

    Return $LatOrt,$LonOrt,$GPSLat,$GPSLon
  }

  Function Get-AddressBasedOnLatitudeAndLongitude
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-August-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-AddressBasedOnLatitudeAndLongitude
    =============================================================================================================================================
    .SYNOPSIS

    This function finds the address based on latitude and longitude.
    It is also called reversed geocoding.

    #>

    Param
     (
      [String] $Latitude,
      [string] $Longitude
     )

     $Result  = Invoke-WebRequest "https://maps.googleapis.com/maps/api/geocode/json?latlng=$Latitude,$Longitude&key=$Global:gblGoogleAPIKey" -ContentType "application/json; charset=Unicode"| ConvertFrom-Json

     if($Result.status -eq "OK")
      {
       $MostAccurateAddress = $Result.results.formatted_address[0]
     
       Add-EntryToLogFile -Entry "    The Google API Key has been enabled for reversed geocoding."
       Add-EntryToLogFile -Entry "    Found address: $MostAccurateAddress"
       Return $MostAccurateAddress
      }
       elseif($Result.status -eq "REQUEST_DENIED")
      {
       Add-EntryToLogFile -Entry "    The given Google API Key has not been enabled for reversed geocoding."
       Add-EntryToLogFile -Entry "    Please perfrom the following steps to enable reversed geocoding:"
       Add-EntryToLogFile -Entry "      1. Go to https://console.cloud.google.com/apis/dashboard"
       Add-EntryToLogFile -Entry "      2. Click on ENABLE APIS AND SERVICES"
       Add-EntryToLogFile -Entry "      3. In the 'Search for APIs & Services' enter 'Geocoding API'."
       Add-EntryToLogFile -Entry "      4. Click on Geocoding API"
       Add-EntryToLogFile -Entry "      5. Click on the [Enable] button."
      }
       elseif($Result.status -eq "ZERO_RESULTS")
      {
       $Message = "Reverse geocoding was successful but returned no results."
       Add-EntryToLogFile -Entry $Message
       Return $Message
      }

      
      Add-EntryToLogFile -Entry "Reversed geocoding came back with the error: '$($Result.Status)'."

      Return ""
  }

  Function Get-FolderFromButton
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       26-January-2021 / Updated 09-February-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-FolderFromButton
    =============================================================================================================================================
    .SYNOPSIS

    You can browse to a folder when clicked on the 'Browse' button.
    #>

    Param
    (
     [String] $InitialDirectory,
     [String] $Description,
     [Switch] $AllowNewFolder
    )

    $Folder                         = New-Object System.Windows.Forms.FolderBrowserDialog
    $Folder.ShowNewFolderButton     = $AllowNewFolder
    $Folder.SelectedPath            = $InitialDirectory
    $Folder.Description             = $Description
    [void]$Folder.ShowDialog()
    Return $Folder.SelectedPath
   }

  Function Process-AllthePictures
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       29-May-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Process-AllthePictures
    =============================================================================================================================================
    .SYNOPSIS

    Process all the pictures in the given folder.
    #>

    Param
     (
      [String] $HTMLFileToWrite,
      [Switch] $GUI,
      [Switch] $SaveAsPDFFile
     )
     

  # =============================================================================================================================================
  # Check if there is a working internet connection. It will check if Google can be reached.
  # =============================================================================================================================================

    $InternetConnection     = WorkingInternetConnection

  # =============================================================================================================================================
  # Define header and the various files that are used within the website. 
  # =============================================================================================================================================

    $Header      = ""
    $VarOptions  = "" 
    $VarMap      = ""
    $Marker      = ""
    If($Global:gblGoogleAPIKey -and $InternetConnection)
     {
      $Header     += "  <script src=$([char]34)https://polyfill.io/v3/polyfill.min.js?features=default$([char]34)></script>`n"
      $Header     += "     <script`n"
      $Header     += "        src="/joomla/+ $([char]34) +"https://maps.googleapis.com/maps/api/js?key=$($Global:gblGoogleAPIKey)&callback=initialize&libraries=&v=weekly$([char]34)`n"
      $Header     += "        defer`n"
      $Header     += "   ></script>`n"
      $Header     += "  <script>`n"
      $Header     += "  function initialize() {`n"
     }

    $CSS         = "<style type=$([char]34)text/css$([char]34)>`n"
    $CSS        += "table  {`n"
    $CSS        += "        font-size:       12px`n"
    $CSS        += "        border:          0px;`n"
    $CSS        += "        font-family:     Arial, Helvetica, sans-serif;`n"
    $CSS        += "       }`n`n"
    $CSS        += "td     {`n"
    $CSS        += "        padding:        4px;`n"
    $CSS        += "        margin:         0px;`n"
    $CSS        += "        border:         1px;`n"
    $CSS        += "        width:          400px;`n"
    $CSS        += "        vertical-align: top;`n"
    $CSS        += "       }`n`n"
    $CSS        += "th     {`n"
    $CSS        += "        background:     #395870;`n"
    $CSS        += "        background:     linear-gradient(#49708f, #293f50);`n"
    $CSS        += "        color:          #fff;`n"
    $CSS        += "        font-size:      11px;`n"
    $CSS        += "        padding:        10px 15px;`n"
    $CSS        += "        vertical-align: top;`n"
    $CSS        += "       }`n`n"
    $CSS        += "tr     {`n"
    $CSS        += "        width:          1000px;`n"
    $CSS        += "       }`n`n"
    $CSS        += "h1     {`n"
    $CSS        += "        font-family:    Arial, Helvetica, sans-serif;`n"
    $CSS        += "        color:          #e68a00;`n"
    $CSS        += "        font-size:      28px;`n"
    $CSS        += "        text-align:     center;`n"
    $CSS        += "       }`n`n"
    $CSS        += "h2     {`n"
    $CSS        += "        font-family:    Arial, Helvetica, sans-serif;`n"
    $CSS        += "        color:          #000099;`n"
    $CSS        += "        font-size:      16px;`n"
    $CSS        += "        text-align:     center;`n"
    $CSS        += "       }`n`n"
    $CSS        += "#Cred  {`n"
    $CSS        += "        font-family:    Arial, Helvetica, sans-serif;`n"
    $CSS        += "        color:          #0000ff;`n"
    $CSS        += "        font-size:      12px;`n"
    $CSS        += "        text-align:     left;`n"
    $CSS        += "       }`n`n"
    $CSS        += "#Alert {`n"
    $CSS        += "        font-family:    Arial, Helvetica, sans-serif;`n"
    $CSS        += "        color:          red;`n"
    $CSS        += "        font-size:      14px;`n"
    $CSS        += "        text-align:     left;`n"
    $CSS        += "       }`n`n"

   
  # =============================================================================================================================================
  # Read the directory with all the file names with the *.jpg or *.jpeg extension.
  # The -filter option can only contain one extension. 
  # =============================================================================================================================================

    $JPGFileNames  = @(Get-ChildItem -Path $Global:gblDirectory -Filter *.jpg  -Recurse:$Global:gblIncludeSubFolders)
    $JPGFileNames += @(Get-ChildItem -Path $Global:gblDirectory -Filter *.jpeg -Recurse:$Global:gblIncludeSubFolders)

    $JPGFileNames  = $JPGFileNames | Sort-Object

    if($Global:gblShowOnlyUniquePictures)
     {
      $JPGFileNames = $JPGFileNames | Get-Unique
     }
  
  # =============================================================================================================================================
  # Go through all the jpg files.
  # =============================================================================================================================================

    $TotalJPGFiles     = $JPGFileNames.Count
    $valCounter        = 1
    $Step              = $TotalJPGFiles / 100
    $ArrayFilesToClose = @()

    ForEach ($JPGFileName in $JPGFileNames)
     {
      Add-EntryToLogFile -Entry "Processing picture: $($JPGFileName.FullName)"
      if($GUI)
       {
        $pgbOverlay.Value                 = $valCounter / $TotalJPGFiles * 100
        $pgbOverlay.Update()
        $lblProcessing.Text               = "Processing file: $($JPGFileName.Name) (file $valCounter of $TotalJPGFiles files)"
        $lblProcessing.Update()
        $Orientation                      = Get-Orientation -SourceFileName $($JPGFileName.FullName)
        $pbPicturePreview.Image           = [System.Drawing.Bitmap]$($JPGFileName.FullName)
        if($Orientation -eq 2) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipX)}
        if($Orientation -eq 3) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipXY)}
        if($Orientation -eq 4) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipY)}
        if($Orientation -eq 5) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipX)}
        if($Orientation -eq 6) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipNone)}
        if($Orientation -eq 7) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipY)}
        if($Orientation -eq 8) {$pbPicturePreview.Image.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipXY)}
        $pbPicturePreview.Update()
       }
        else
       {
        Write-Progress -Id 1 -Activity "Processing all the jpg files." -Status "Processing file $JPGFileName   (Number $valCounter of $TotalJPGFiles)" -PercentComplete $($valCounter / $TotalJPGFiles * 100)
       }
       
      $tmpPhotoData = (Get-PhotoMetaDataFromJPGFile -FileName $($JPGFileName.FullName))
      $tmpPicture   = "<img src=$([char]34)$($JPGFileName.FullName)$([char]34) style=$([char]34)width:600px;$([char]34)>`n"
      $tmpString    = "00000000$valCounter"
      $Picture      = "$PrefixCSS$($tmpString.Substring($tmpString.Length-8))"
    
      Add-EntryToLogFile -Entry "    Image source: $tmpPicture"
      if($InternetConnection)
       {
        If($Global:gblGoogleAPIKey)
         {
          $Latitude,$Longitude,$GPSLatitude,$GPSLongitude = Get-EXIFDataFromJPGFile -FileName $($JPGFileName.FullName)
          if(-not($Latitude -eq "1024" -and $Longitude -eq "1024"))
           {
            Add-EntryToLogFile -Entry "    A valid latitude and longitude have been found in this picture."
            $Header     += " var my$Picture = new google.maps.LatLng(" + $Latitude + "," + $Longitude + ");`n"
            $Address     = Get-AddressBasedOnLatitudeAndLongitude -Latitude $Latitude -Longitude $Longitude 

            $VarOptions += " var Options$Picture = {`n"
            $VarOptions += " zoom:           17,`n"
            $VarOptions += " center:         my$Picture,`n"
            $VarOptions += " mapTypeId:      google.maps.MapTypeId.ROADMAP,`n"
            $VarOptions += " mapTypeControl: 0`n"
            $VarOptions += "}`n"

            $VarMap     += " var map$Picture = new google.maps.Map(document.getElementById('map-$Picture'), Options$Picture);`n"

            $Marker     += " var Marker$Picture = new google.maps.Marker({`n"
            $Marker     += " position: my$Picture,`n"
            $Marker     += " map:      map$Picture,`n"
            $Marker     += " title:    '$Picture'`n"
            $Marker     += "});`n"

            $CSS        += " #map-$Picture {`n"
            $CSS        += "                            width:            400px;`n"
            $CSS        += "                            height:           400px;`n"
            $CSS        += "                            background-color: #CCC;`n"
            $CSS        += "                           }`n`n"

            $tmpGoogleMapsMap  = "<div id=$([char]34)map-$Picture$([char]34)"
            $tmpGoogleMapsMap += "></div>`n"
            $tmpGoogleMapsMap += "<p>Address: $Address</p>"
            $tmpGoogleMapsMap += "<p>GPS Coordinates:<br>"
            $tmpGoogleMapsMap += "  $GPSLatitude<br>"
            $tmpGoogleMapsMap += "  $GPSLongitude</p>"
           }
           else
           {
            $ErrorMessage = "There are no coordinates found in the picture."
            Add-EntryToLogFile -Entry "    * Error: $ErrorMessage"
            $tmpGoogleMapsMap = $ErrorMessage
           }
         }
         else
         {
         Add-EntryToLogFile -Entry "   * Error: No Google Maps API Provided. Specify one as a parameter or register one via https://developers.google.com/maps/documentation/javascript/get-api-key and use it as a parameter"
         $tmpGoogleMapsMap  = "There is no Google Maps API provided.<br>"
         $tmpGoogleMapsMap += "Now, there are two options:<br>"
         $tmpGoogleMapsMap += "<ol>"
         $tmpGoogleMapsMap += "  <li>Use the parameter <b>GoogleAPIKey</b> and provide the APIKey.</li>"
         $tmpGoogleMapsMap += "  <li>Register a new Google API Key via <a href=$([char]34)https://developers.google.com/maps/documentation/javascript/get-api-key$([char]34)>Get an API Key on Google Maps Platform</a>.</li>"
         $tmpGoogleMapsMap += "</ol>"
       }    
     }
        else
       {
        $tmpGoogleMapsMap  = "There is no working internet connection. So no Google Maps information.<br>"
       }
    
      Add-EntryToHTMLTable -Picture $tmpPicture -PhotoData $tmpPhotoData -GoogleMapsMap $tmpGoogleMapsMap
      $valCounter++
     }

  # =============================================================================================================================================
  # Format the HTML Header
  # =============================================================================================================================================

    $Header  = $Header + "`n" + $VarOptions + "`n"+ $VarMap + "`n"+ $Marker
    If($Global:gblGoogleAPIKey -and $InternetConnection)
     {
      $Header += "}`n"
      $Header += "</script>`n`n"
     }
    $Header += $CSS
    $Header += "</style>"
    $Header += ""

    Add-EntryToLogFile -Entry "   The header: `n$Header"

  # =============================================================================================================================================
  # Load all the DLL files that are needed for IText7 HTML To PDF
  # See https://kb.itextsupport.com/home/it7kb/ebooks/itext-7-converting-html-to-pdf-with-pdfhtml for more information.
  # =============================================================================================================================================

    $DLLFilesLoadedSuccessfully = $True  
    if($SaveAsPDFFile)
     {
      if($Global:gblDLLPathExists)
       {
        $DLLFiles = Get-ChildItem -Path $Global:gblDLLPath -Filter *.dll -File
        ForEach ($DLLFile in $DLLFiles)
         {
          Try
           {
            Add-Type -Path $($DLLFile.FullName)
            Add-EntryToLogFile "The DLL file '$($DLLFile.FullName)' has been loaded successfully." 
           }
            Catch
           {
            $DLLFilesLoadedSuccessfully = $False
            Add-EntryToLogFile "Failure while loading the DLL file '$($DLLFile.FullName)': $($_.Exception.Message)."
           }
         }
       }
        else
       {
        Add-EntryToLogFile -Entry "The path '$Global:gblDLLPath' does not exists. As these DLL's are needed, the PDF file cannot be created."
       }
     }

  # =============================================================================================================================================
  # It appeared that the table layout was scrambled with - for example -
  # <td>&lt;img src=&quot;.\0.jpg&quot; style=&quot;width:600px;&quot;&gt;</td>
  # Add-Type -AssemblyName System.Web and
  # [System.Web.HttpUtility]::HtmlDecode($HTMLTable)
  # solved this issue.
  # Source: https://stackoverflow.com/questions/23143393/powershell-convertto-html-is-translating-and-symbols-to-lt-and-gt-how 
  #
  # Create the html table
  # =============================================================================================================================================

    if($SaveAsPDFFile)
     {
      $HTMLTable = ""
      ForEach ($Record in $Global:arrHTMLTable)
       {
        $HTMLTable   += $Record | ConvertTo-Html -Fragment -As List
        $HTMLTable   += "<br>"
       }
       $HTMLTable = $HTMLTable -replace ("<table>","<table width=100%>")
       $HTMLTable = $HTMLTable -replace ("<tr><td>","<tr><td width=20%>")
       $HTMLTable = $HTMLTable -replace ("</td><td>","</td><td width=80%>")
       $HTMLTable = $HTMLTable -replace ("</table>","</table>`n")
     }
      else
     {
      $HTMLTable = $Global:arrHTMLTable | ConvertTo-Html -Property Picture,"Photo Data","Google Maps Map" -Fragment -PreContent "<h2>The pictures</h2>"
     }
    
    $HTMLTable = [System.Web.HttpUtility]::HtmlDecode($HTMLTable)

  # =============================================================================================================================================
  # Create the whole page.
  # =============================================================================================================================================

    $Title       = "<h1>Complete overview of the pictures used</h1>"

    $Timestamp   = (Get-Date -UFormat "%a %e %b %Y %X").ToString()
    $PostContent = "<p id=$([char]34)Cred$([char]34)>Creation Date: $Timestamp</p>"
    $Report      = ConvertTo-Html -Head $Header -Body "$Title $HTMLTable" -PostContent $PostContent 


    if($SaveAsPDFFile)
     {
      if($Global:gblDLLPathExists)
       {
        if($DLLFilesLoadedSuccessfully)
         {
          $PDFFileToWrite  = [System.IO.FileInfo]::new($HTMLFileToWrite -replace("html","pdf"))
          $HTMLFileToWrite = $env:TEMP + "\TempHTMLFile.html"
          $Report | Out-File $HTMLFileToWrite
          $HTMLInputFile   = [System.IO.FileInfo]::new($HTMLFileToWrite)
          [iText.Html2Pdf.HtmlConverter]::ConvertToPdf($HTMLInputFile, $PDFFileToWrite)
          $tmpLine         = "The PDF file '$PDFFileToWrite' has been created."
          Add-EntryToLogFile -Entry $tmpLine
          if($GUI)
           {
            Display-MessageBox -GUI -Text $tmpLine
           }
            else
           {
            Display-MessageBox -Text $tmpLine
           }
         # Remove-Item -Path $HTMLFileToWrite
         }
          else
         {
          $tmpLine = "There where errors while loading the DLL files from '$Global:gblDLLPath'. So no PDF file is created."
          Add-EntryToLogFile -Entry $tmpLine
          if($GUI)
           {
            Display-MessageBox -GUI -Text $tmpLine
           }
            else
           {
            Display-MessageBox -Text $tmpLine
           }
         }
       }
        else
       {
        Add-EntryToLogFile -Entry "The path '$Global:gblDLLPath' does not exists. So the PDF file is not created as it needs files in that directory."
       }
     }
      else
     {
      $Report | Out-File $HTMLFileToWrite
      $tmpLine = "The webpage '$HTMLFileToWrite' has been created."
      Add-EntryToLogFile -Entry $tmpLine
      if($GUI)
       {
        Display-MessageBox -GUI -Text $tmpLine
       }
        else
       {
        Display-MessageBox -Text $tmpLine
       }
     }

   }

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

# =============================================================================================================================================
# Declares the variables.
# =============================================================================================================================================

  Clear-host
  
  $Global:arrHTMLTable                = @()
  $Global:gblDetailedLogging          = $DetailedLogging
  $Global:gblLogFile                  = ""
  $Global:gblSilent                   = $Silent
  $Global:gblDLLPath                  = $(Split-Path $MyInvocation.MyCommand.Definition) + "\DLLFilesForItext7"
  $Global:gblDLLPathExists            = $False
  $MinimumWidth                       = 800
  $MinimumHeight                      = 700
  $FormHeader                         = "Show picture with details (v0.1)"
  $RegistryKey                        = "HKCU:Software\VroomSoft\PictureDetails"
  $Message                            = ""

  if($ClearUserSettings -and (Test-Path $RegistryKey))
   {
    Remove-Item -Path $RegistryKey -Force | Out-Null
   }

  if(-not($Silent))
   { 
    if(-not($NoUserSettings))
     {  

      Add-EntryToLogFile -Entry "Reading the user settings from the registry: $RegistryKey."

      if(-not($Global:gblGoogleAPIKey))
       {
        $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey"
        if($tmpValue1) {$Global:gblGoogleAPIKey                  = $tmpValue1} else {$Global:gblGoogleAPIKey           = $GoogleAPIKey}
       }

      if(-not($Global:Directory))
       {
        $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "Directory"
        if($tmpValue1) {$Global:gblDirectory                     = $tmpValue1} else {$Global:gblDirectory              = $Directory}
       }

      if($Global:IncludeSubFolders -eq "")
       {
        $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "SubFolders"
        if($tmpValue1) {[bool]$Global:gblIncludeSubFolders       = $tmpValue1} else {$Global:gblIncludeSubFolders      = $IncludeSubFolders}
       }
      
      if($Global:ShowOnlyUniquePictures -eq "")
       {
        $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "UniquePictures"
        if($tmpValue1) {[bool]$Global:gblShowOnlyUniquePictures  = $tmpValue1} else {$Global:gblShowOnlyUniquePictures = $ShowOnlyUniquePictures}
       }
     }
   }
   else
   {
   $Global:gblShowOnlyUniquePictures = $ShowOnlyUniquePictures
   $Global:gblIncludeSubFolders      = $IncludeSubFolders
   $Global:gblGoogleAPIKey           = $GoogleAPIKey
   $Global:gblDirectory              = $Directory
   }

  
  if($LogPath -eq "")
   {
    $LogPath   = Split-Path -parent $MyInvocation.MyCommand.Definition
   }
    else
   {
    $Returntext, $bolResult = Create-Folder -FolderName $LogPath
    if($bolResult)
     {  
      $Message = $Returntext
     }
      else
     {
      Write-Error $Returntext -Category WriteError
      Exit 2
     }
   }

  if(Test-Path $Global:gblDLLPath)
   {
    $Global:gblDLLPathExists = $True
   } 

# =============================================================================================================================================
# Add assemblies.
# =============================================================================================================================================

  Add-Type -AssemblyName System.Web                | Out-Null
  Add-Type -AssemblyName System.Drawing            | Out-Null
  Add-Type -AssemblyName System.Windows.Forms      | Out-Null
  Add-Type -AssemblyName PresentationFramework     

# =============================================================================================================================================
# Define the HTML File.
# =============================================================================================================================================
 
  $LastPartOfHTMLFile = " (" + (Get-Date).ToString('G') + ").html"
  $LastPartOfHTMLFile = $LastPartOfHTMLFile -replace ":","-"
  $LastPartOfHTMLFile = $LastPartOfHTMLFile -replace "/","-"
  $PreFixLogFile      = "Pictures"
  $PrefixCSS          = "Picture"

  $HTMLFile   = $LogPath + "\"+ $PreFixLogFile + $LastPartOfHTMLFile

# =============================================================================================================================================
# Define the log File.
# =============================================================================================================================================

  if($Global:gblDetailedLogging)
   {
    $Global:gblLogFile = $LogPath + "\"+ $PreFixLogFile + $($LastPartOfHTMLFile -replace ".html",".log")
    New-Item $Global:gblLogFile -ItemType File -Force | Out-Null
   }

# =============================================================================================================================================
# Find all the arguments and put them in the log file
# Source: https://ss64.com/ps/psboundparameters.html
# =============================================================================================================================================

  Add-EntryToLogFile -Entry  "---------------------------------------------------------------------------------------------------------------"
  Add-EntryToLogFile -Entry  "Used parameters:" 
  ForEach($boundparam in $PSBoundParameters.GetEnumerator()) 
   {
    Add-EntryToLogFile -Entry " * Key: $($boundparam.Key) Value: $($boundparam.Value)" 
   }
  Add-EntryToLogFile -Entry  "---------------------------------------------------------------------------------------------------------------"

# =============================================================================================================================================
# Check if there is a working internet connection. It will check if Google can be reached.
# =============================================================================================================================================

  $InternetConnection     = WorkingInternetConnection

# =============================================================================================================================================
# Define the directory where the photo's are located.
# =============================================================================================================================================  
  
  if(-not $Global:gblDirectory)
   {
    $Global:gblDirectory     = Split-Path -parent $MyInvocation.MyCommand.Definition
    Add-EntryToLogFile -Entry "The Directory path has been changed to $Global:gblDirectory."
   }

# =============================================================================================================================================
# Check the screenwidth and screenheight. If too small than falling back to silent mode.
# Source: https://stackoverflow.com/questions/7967699/get-screen-resolution-using-wmi-powershell-in-windows-7
# =============================================================================================================================================

  $DetectedWidth  = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Width
  $DetectedHeight = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Height

  if(($DetectedWidth -le $MinimumWidth) -or ($DetectedHeight -le $MinimumHeight))
   {
    $tmpLine = ""
    if($DetectedWidth -le $MinimumWidth)
     {
      $tmpLine = "The screenwidth of $DetectedWidth is less than or equal to $MinimumWidth."
      Add-EntryToLogFile -Entry $tmpLine
      Add-EntryToLogFile -Entry "Falling back to silent mode."
      $Global:gblSilent = $True
     }

    if($DetectedHeight -le $MinimumHeight)
     {
      $tmpLine1 = "The screenheight of $DetectedHeight is less than or equal to $MinimumHeight."
      Add-EntryToLogFile -Entry $tmpLine1
      Add-EntryToLogFile -Entry "Falling back to silent mode."
      $Global:gblSilent = $True
      if(-not($tmpLine))
       {
        $tmpLine = $tmpLine1
       } 
        else
       {
        $tmpLine += "`n$tmpLine1"
       }
     }
    $tmpLine += "`nFalling back to silent mode."
    Display-MessageBox -Text $tmpLine -GUI -Error
   }
 
# =============================================================================================================================================
# Do the job. 
# =============================================================================================================================================  

  if($Global:gblSilent)
   {

  # =============================================================================================================================================
  # Silent mode.
  # =============================================================================================================================================  

    Process-AllthePictures -SaveAsPDFFile:$SaveAsPDF -HTMLFileToWrite $HTMLFile
   }
    else
   {

  # =============================================================================================================================================
  # Non Silent mode, thus with GUI.
  # =============================================================================================================================================  

  #----------------------------------------------
  # region Define SAPIEN Types
  #----------------------------------------------
  
   if ($PSVersionTable.PSVersion.Major -ge 7)
    {
     $Assemblies = 'System.Windows.Forms', 'System.Drawing', 'System.Drawing.Primitives', 'System.ComponentModel.Primitives', 'System.Drawing.Common', 'System.Runtime'
    }     
   else
    {
     $Assemblies = 'System.Windows.Forms', 'System.Drawing'  
    }
    
   Add-Type -ReferencedAssemblies $Assemblies -TypeDefinition @"
using System;
using System.Windows.Forms;
using System.Drawing;
        namespace SAPIENTypes
        {
    public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
        {
                public ProgressBarOverlay() : base() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); }
            protected override void WndProc(ref Message m)
            { 
                base.WndProc(ref m);
                if (m.Msg == 0x000F)// WM_PAINT
                {
                    if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                        {
                            using (Graphics g = this.CreateGraphics())
                            {
                                using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                {
                                    stringFormat.Alignment = StringAlignment.Center;
                                    stringFormat.LineAlignment = StringAlignment.Center;
                                    if (!string.IsNullOrEmpty(this.Text))
                                        g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    else
                                    {
                                        int percent = (int)(((double)Value / (double)Maximum) * 100);
                                        g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    }
                                }
                            }
                        }
                }
            }
              
                public string TextOverlay
                {
                    get
                    {
                        return base.Text;
                    }
                    set
                    {
                        base.Text = value;
                        Invalidate();
                    }
                }
        }
        }
"@ -IgnoreWarnings | Out-Null
   

  # endregion Define SAPIEN Types


  # =============================================================================================================================================
  # Forms block (main form)
  # =============================================================================================================================================

    [System.Windows.Forms.Application]::EnableVisualStyles()
    $frmTestPicture                              = New-Object 'System.Windows.Forms.Form'
    $btnSaveASPDF                                = New-Object 'System.Windows.Forms.Button'
    $chkUniquePicturesOnly                       = New-Object 'System.Windows.Forms.CheckBox'
    $btnBrowse                                   = New-Object 'System.Windows.Forms.Button'
    $chkIncludeSubfolders                        = New-Object 'System.Windows.Forms.CheckBox'
    $txtDirectory                                = New-Object 'System.Windows.Forms.TextBox'
    $lblDirectory                                = New-Object 'System.Windows.Forms.Label'
    $btnSaveAsHTMLPage                           = New-Object 'System.Windows.Forms.Button'
    $btnCancel                                   = New-Object 'System.Windows.Forms.Button' 
    $pgbOverlay                                  = New-Object 'SAPIENTypes.ProgressBarOverlay'
    $lblProcessing                               = New-Object 'System.Windows.Forms.Label'
    $lblGoogleAPIKey                             = New-Object 'System.Windows.Forms.Label'
    $txtGoogleAPIKey                             = New-Object 'System.Windows.Forms.TextBox'
    $pbPicturePreview                            = New-Object 'System.Windows.Forms.PictureBox'

    # frmTestPicture
    #
    $frmTestPicture.Controls.Add($btnSaveASPDF)
    $frmTestPicture.Controls.Add($chkUniquePicturesOnly)
    $frmTestPicture.Controls.Add($btnBrowse)
    $frmTestPicture.Controls.Add($chkIncludeSubfolders)
    $frmTestPicture.Controls.Add($txtDirectory)
    $frmTestPicture.Controls.Add($lblDirectory)
    $frmTestPicture.Controls.Add($btnSaveAsHTMLPage)
    $frmTestPicture.Controls.Add($btnCancel)
    $frmTestPicture.Controls.Add($pgbOverlay)
    $frmTestPicture.Controls.Add($lblProcessing)
    $frmTestPicture.Controls.Add($lblGoogleAPIKey)
    $frmTestPicture.Controls.Add($txtGoogleAPIKey)
    $frmTestPicture.Controls.Add($pbPicturePreview)
    $frmTestPicture.AutoScaleDimensions          = New-Object System.Drawing.SizeF(6, 13)
    $frmTestPicture.AutoScaleMode                = 'Font'
    $frmTestPicture.ClientSize                   = New-Object System.Drawing.Size(784, 661)
    $frmTestPicture.Name                         = 'frmTestPicture'
    $frmTestPicture.StartPosition                = 'CenterScreen'
    $frmTestPicture.Text                         = $FormHeader

    # btnSaveAsHTMLPage
    #
    $btnSaveAsHTMLPage.AccessibleDescription     = ''
    $btnSaveAsHTMLPage.Location                  = New-Object System.Drawing.Point(615, 599)
    $btnSaveAsHTMLPage.Name                      = 'btnSaveAsHTMLPage'
    $btnSaveAsHTMLPage.Size                      = New-Object System.Drawing.Size(75, 50)
    $btnSaveAsHTMLPage.TabIndex                  = 8
    $btnSaveAsHTMLPage.Text                      = 'Run and save as HTML page'
    $btnSaveAsHTMLPage.UseVisualStyleBackColor   = $True
    $btnSaveAsHTMLPage.add_Click({
                                  $Global:gblShowOnlyUniquePictures = $False
                                  $Global:gblIncludeSubFolders      = $False
                                  if($chkUniquePicturesOnly.Checked)  {$Global:gblShowOnlyUniquePictures = $True}
                                  if($chkIncludeSubfolders.Checked)   {$Global:gblIncludeSubFolders      = $True}
                                  Process-AllthePictures -HTMLFileToWrite $HTMLFile -GUI
                                  if(-not($NoUserSettings))
                                   {
                                    ReadAndSetSettings -Set -RegistryKey $RegistryKey
                                   }
                                  $frmTestPicture.Close()
                                  $frmTestPicture.Dispose
                                 })

    # btnSaveASPDF
    #
    $btnSaveASPDF.Location                       = New-Object System.Drawing.Point(534, 599)
    $btnSaveASPDF.Name                           = 'btnSaveASPDF'
    $btnSaveASPDF.Size                           = New-Object System.Drawing.Size(75, 50)
    $btnSaveASPDF.TabIndex                       = 15
    $btnSaveASPDF.Text                           = 'Run and save as PDF file'
    $btnSaveASPDF.UseVisualStyleBackColor        = $True
    $btnSaveASPDF.add_Click({
                             $Global:gblShowOnlyUniquePictures = $False
                             $Global:gblIncludeSubFolders      = $False
                             if($chkUniquePicturesOnly.Checked)  {$Global:gblShowOnlyUniquePictures = $True}
                             if($chkIncludeSubfolders.Checked)   {$Global:gblIncludeSubFolders      = $True}
                             Process-AllthePictures -HTMLFileToWrite $HTMLFile -GUI -SaveAsPDF
                             if(-not($NoUserSettings))
                              {
                               ReadAndSetSettings -Set -RegistryKey $RegistryKey
                              }
                             $frmTestPicture.Close()
                             $frmTestPicture.Dispose
                            })

    # chkUniquePicturesOnly
    #
    $chkUniquePicturesOnly.Location              = New-Object System.Drawing.Point(534, 540)
    $chkUniquePicturesOnly.Name                  = 'chkUniquePicturesOnly'
    $chkUniquePicturesOnly.Size                  = New-Object System.Drawing.Size(156, 24)
    $chkUniquePicturesOnly.TabIndex              = 14
    $chkUniquePicturesOnly.Text                  = 'Show only unique pictures'
    $chkUniquePicturesOnly.UseVisualStyleBackColor = $True
    $chkUniquePicturesOnly.Checked               = $Global:gblShowOnlyUniquePictures

    # btnBrowse 
    # 
    $btnBrowse.Location                          = New-Object System.Drawing.Point(697, 540)
    $btnBrowse.Name                              = 'btnBrowse'
    $btnBrowse.Size                              = New-Object System.Drawing.Size(75, 25)
    $btnBrowse.TabIndex                          = 13
    $btnBrowse.Text                              = 'Browse'
    $btnBrowse.UseVisualStyleBackColor           = $True
    $btnBrowse.add_Click({
                          $txtDirectory.Text = Get-FolderFromButton -InitialDirectory $Global:gblDirectory -Description "Directory with the pictures"
                          $Global:gblDirectory = $txtDirectory.Text
                          })

    # chkIncludeSubfolders
    #
    $chkIncludeSubfolders.Location               = New-Object System.Drawing.Point(534, 570)
    $chkIncludeSubfolders.Name                   = 'chkIncludeSubfolders'
    $chkIncludeSubfolders.Size                   = New-Object System.Drawing.Size(156, 25)
    $chkIncludeSubfolders.TabIndex               = 12
    $chkIncludeSubfolders.Text                   = 'Include subfolders'
    $chkIncludeSubfolders.UseVisualStyleBackColor= $True
    $chkIncludeSubfolders.Checked                = $Global:gblIncludeSubFolders

    # txtDirectory
    #
    $txtDirectory.Location                       = New-Object System.Drawing.Point(106, 513)
    $txtDirectory.Name                           = 'txtDirectory'
    $txtDirectory.Size                           = New-Object System.Drawing.Size(666, 20)
    $txtDirectory.TabIndex                       = 11
    $txtDirectory.Text                           = $Global:gblDirectory

    # lblDirectory
    # 
    $lblDirectory.AutoSize                       = $True
    $lblDirectory.Location                       = New-Object System.Drawing.Point(15, 516)
    $lblDirectory.Name                           = 'lblDirectory'
    $lblDirectory.Size                           = New-Object System.Drawing.Size(52, 13)
    $lblDirectory.TabIndex                       = 10
    $lblDirectory.Text                           = 'Directory:'
    
    # btnCancel
    #
    $btnCancel.AccessibleDescription             = ''
    $btnCancel.Location                          = New-Object System.Drawing.Point(696, 600)
    $btnCancel.Name                              = 'btnCancel'
    $btnCancel.Size                              = New-Object System.Drawing.Size(75, 50)
    $btnCancel.TabIndex                          = 7
    $btnCancel.Text                              = 'Cancel'
    $btnCancel.UseVisualStyleBackColor           = $True
    $btnCancel.add_Click({
                          $frmTestPicture.Close()
                          $frmTestPicture.Dispose
                          })

    # pgbOverlay
    #
    $pgbOverlay.Location                         = New-Object System.Drawing.Point(12, 599)
    $pgbOverlay.Name                             = 'pgbOverlay'
    $pgbOverlay.Size                             = New-Object System.Drawing.Size(516, 50)
    $pgbOverlay.Style                            = 'Continuous'
    $pgbOverlay.TabIndex                         = 6
    

    # lblProcessing
    #
    $lblProcessing.AutoSize                      = $True
    $lblProcessing.Location                      = New-Object System.Drawing.Point(15, 569)
    $lblProcessing.Name                          = 'lblProcessing'
    $lblProcessing.Size                          = New-Object System.Drawing.Size(210, 13)
    $lblProcessing.TabIndex                      = 5
    
    # lblGoogleAPIKey
    #
    $lblGoogleAPIKey.AutoSize                    = $True
    $lblGoogleAPIKey.Location                    = New-Object System.Drawing.Point(15, 490)
    $lblGoogleAPIKey.Name                        = 'lblGoogleAPIKey'
    $lblGoogleAPIKey.Size                        = New-Object System.Drawing.Size(85, 13)
    $lblGoogleAPIKey.TabIndex                    = 3
    $lblGoogleAPIKey.Text                        = 'Google API Key:'

    # txtGoogleAPIKey
    #
    $txtGoogleAPIKey.Location                    = New-Object System.Drawing.Point(106, 487)
    $txtGoogleAPIKey.Name                        = 'txtGoogleAPIKey'
    $txtGoogleAPIKey.Size                        = New-Object System.Drawing.Size(666, 20)
    $txtGoogleAPIKey.TabIndex                    = 1
    $txtGoogleAPIKey.Text                        = $Global:gblGoogleAPIKey

    
    # pbPicturePreview
    #
    $pbPicturePreview.Location                   = New-Object System.Drawing.Point(15, 25)
    $pbPicturePreview.Name                       = 'pbPicturePreview'
    $pbPicturePreview.Size                       = New-Object System.Drawing.Size(757, 437)
    $pbPicturePreview.SizeMode                   = 'Zoom'
    $pbPicturePreview.TabIndex                   = 0
    $pbPicturePreview.TabStop                    = $False

    if(-not($Global:gblDLLPathExists))
     {
      $btnSaveASPDF.Enabled = $False
      $Global:gblSaveAsPDF  = $False
     }
    [void]$frmTestPicture.ShowDialog()

  # =============================================================================================================================================
  # End forms block (main form)
  # =============================================================================================================================================


   }

 

Attachments:
Download this file (ExamplePictureBox (no rotation).7z)ExamplePictureBox (no rotation)[ExamplePictureBox (no rotation)]60 kB
Download this file (ExamplePictureBox (rotation).7z)ExamplePictureBox (rotation)[ExamplePictureBox (rotation)]61 kB
Download this file (PictureDetailsWithForms_v01.7z)PictureDetailsWithForms_v01[PictureDetailsWithForms_v01]6912 kB