Introduction

In 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 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 were not shown in the correct rotation. So you had to turn your head to see the pictures correctly. 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 relatively 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 to 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.

I made some significant improvements in version 0.5:

There were 2 significant bugs: if you select the option to [SaveAsPDF]:

  1. The picture was not rotated.
  2. Google maps information was displayed in a grey box.

The first one kept me busy for a long time. I use itext7 HTMLtoPDF to create a PDF file from an HTML file. It appears that iText7 cannot handle the orientation bit in a JPG file. So, I had to work around this issue:

  • The first option I tried was to use CSS with  transform: rotate(90deg);. That has a significant disadvantage: you must have a square box with the same width and height. Otherwise, the rotation could not be done. Then, you had to tweak the position with an object-position. And the object-position is something that iText7 cannot handle.
  • The second option was an alternative to iText7. But those were too expensive or not useful.
  • The third and final option: use a base64 image and rotate that base64 image. Then, there is no rotation information available, and iText7 can create the HTML file.
    <img src="data:image/jpeg;base64,<<Image in Base64 format>>" width=300px>
    I even use the Base64 image in the HTML file if no PDF is created. 

Also, I removed the ShowOnlyUniquePictures and replaced it with three radio buttons. 

Significant improvements in version 1.0:

A new feature is to set the maptype and zoom factor of a Google Maps picture:

P12 Set Google Maps zoom and maptype 

This information is stored in the registry. So you adjust the Google Maps information to your needs. 

You can test it with the following code:

  Function Has-ValidGoogleAPIKey
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       20-Nov-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Has-ValidGoogleAPIKey
    =============================================================================================================================================
    .SYNOPSIS

    This function checks the Google API Key.
    As the Google API Key is in a Global variable, there is no need to pass it as a parameter

    #>

    $URL         = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&size=100x100"
    $Result      = $null
    $ReturnValue = $True
    Try
     {
      If ($ProxyServer)
       {
        $Result  = Invoke-WebRequest -Uri $URL -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials -Method Get -UseBasicParsing
       }
        else
       {
        $Result  = Invoke-WebRequest -Uri $URL -Method Get -UseBasicParsing
       }
      }
       Catch
      {
       $ErrorMessage = "There is an error: $($_.Exception.Message)"
       Write-Host "    * Error while creating the Google Maps map: '$ErrorMessage'."
       Write-Host "      Check your Google API key '$Global:gblGoogleAPIKey', that might be incorrect."
      }

   if (-not $Result)
    {
     $ReturnValue = $False
    }

   Return $ReturnValue

   }
  
  Function Get-ZoomFactorAndMapLayout
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-Nov-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-ZoomFactorAndMapLayout
    =============================================================================================================================================
    .SYNOPSIS

    This function gives the user the chance to set the:
     - zoomfactor for Google Maps pictures
     - mapinformation for Google Maps pictures

    #>

    Param
     (
      [Parameter(Mandatory=$false)] 
      [int]      $Zoom              = 17,

      [Parameter(Mandatory=$False)] 
      [ValidateSet("roadmap","satellite","terrain","hybrid")]
      [String[]] $MapType           = "roadmap"
     )

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

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

  # =============================================================================================================================================
  # Declare variables
  # =============================================================================================================================================
    
    [int]      $MapsWidth         = 380
    [int]      $MapsHeight        = 380
    [String]   $URL               = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
    [String[]] $MapDetailsItems   = @("roadmap","satellite","terrain","hybrid")


  # =============================================================================================================================================
  # Make the form
  # =============================================================================================================================================
    
    $frmGoogleMapsZoom = New-Object 'System.Windows.Forms.Form'
    $webSample         = New-Object 'System.Windows.Forms.WebBrowser'
    $trkZoom           = New-Object 'System.Windows.Forms.TrackBar'
    $btnReset          = New-Object 'System.Windows.Forms.Button'
    $btnOk             = New-Object 'System.Windows.Forms.Button'
    $cboMapDetails     = New-Object 'System.Windows.Forms.ComboBox'
    $ToolTipsGUI       = New-Object 'System.Windows.Forms.ToolTip'
    $txtTrackbar       = New-Object 'System.Windows.Forms.TextBox'
        
    # frmGoogleMapsZoom
    #
    $frmGoogleMapsZoom.Controls.Add($btnReset)
    $frmGoogleMapsZoom.Controls.Add($btnOk)
    $frmGoogleMapsZoom.Controls.Add($webSample)
    $frmGoogleMapsZoom.Controls.Add($trkZoom)
    $frmGoogleMapsZoom.Controls.Add($cboMapDetails)
    $frmGoogleMapsZoom.Controls.Add($txtTrackbar)
    $frmGoogleMapsZoom.AutoScaleMode          = 'None'
    $frmGoogleMapsZoom.ClientSize             = New-Object System.Drawing.Size(457, 568)
    $frmGoogleMapsZoom.Name                   = 'frmGoogleMapsZoom'
    $frmGoogleMapsZoom.StartPosition          = 'CenterScreen'
    $frmGoogleMapsZoom.Text                   = 'Test Google API Zoom'

    # ToolTipsGUI
    # 
    $ToolTipsGUI.AutomaticDelay               = 500
    $ToolTipsGUI.AutoPopDelay                 = 5010
    $ToolTipsGUI.InitialDelay                 = 500
    $ToolTipsGUI.IsBalloon                    = $True
    $ToolTipsGUI.ReshowDelay                  = 100
    $ToolTipsGUI.Tag                          = 'Information'
    $ToolTipsGUI.ToolTipIcon                  = 'Info'
    $ToolTipsGUI.ToolTipTitle                 = "Additional information"

    # txtTrackbar
    #
    $txtTrackbar.Location                     = New-Object System.Drawing.Point(168, 450)
    $txtTrackbar.Name                         = 'txtTrackbar'
    $txtTrackbar.Size                         = New-Object System.Drawing.Size(26, 20)
    $txtTrackbar.TabIndex                     = 5
    $txtTrackbar.Readonly                     = $True
    $txtTrackbar.Text                         = $Zoom
 
    # cboMapDetails
    #
    $cboMapDetails.FormattingEnabled          = $True
    $cboMapDetails.Location                   = New-Object System.Drawing.Point(22, 452)
    $cboMapDetails.Name                       = 'cboMapDetails'
    $cboMapDetails.Size                       = New-Object System.Drawing.Size(121, 21)
    $cboMapDetails.TabIndex                   = 4
    $cboMapDetails.Dropdownstyle              = 'DropDownList'
    [void] $cboMapDetails.Items.AddRange($MapDetailsItems)
    $cboMapDetails.Text                       = $MapType

    # btnReset
    #
    $btnReset.Location                        = New-Object System.Drawing.Point(200,515)
    $btnReset.Name                            = 'btnReset'
    $btnReset.Size                            = New-Object System.Drawing.Size(100, 45)
    $btnReset.TabIndex                        = 3
    $btnReset.Text                            = 'Reset'
    $btnReset.UseVisualStyleBackColor         = $True
    $ToolTipsGUI.SetToolTip($btnReset, "Reset the settings to its default: zoomfactor = 17 and map type is roadmap.")

    # btnOk
    #
    $btnOk.Location                           = New-Object System.Drawing.Point(322, 515)
    $btnOk.Name                               = 'btnOk'
    $btnOk.Size                               = New-Object System.Drawing.Size(100, 45)
    $btnOk.TabIndex                           = 2
    $btnOk.Text                               = 'Ok'
    $btnOk.UseVisualStyleBackColor            = $True
    $btnOk.DialogResult                       = 'OK'
    $ToolTipsGUI.SetToolTip($btnOk, "Accept the values.")
  
    # webSample
    #
    $webSample.AllowWebBrowserDrop            = $False
    $webSample.IsWebBrowserContextMenuEnabled = $False
    $webSample.Location                       = New-Object System.Drawing.Point(22, 37)
    $webSample.Margin                         = '10, 10, 10, 10'
    $webSample.MaximumSize                    = New-Object System.Drawing.Size(400, 400)
    $webSample.MinimumSize                    = New-Object System.Drawing.Size(400, 400)
    $webSample.Name                           = 'webSample'
    $webSample.ScrollBarsEnabled              = $False
    $webSample.Size                           = New-Object System.Drawing.Size(400, 400)
    $webSample.TabIndex                       = 1
    $webSample.Url                            = $URL
    $webSample.WebBrowserShortcutsEnabled     = $False
    $WebSample.Anchor                         = 'Top, Bottom, Left, Right'

    # trkZoom
    #
    $trkZoom.Location                         = New-Object System.Drawing.Point(200, 450)
    $trkZoom.Maximum                          = 20
    $trkZoom.Minimum                          = 1
    $trkZoom.Name                             = 'trkZoom'
    $trkZoom.Size                             = New-Object System.Drawing.Size(222, 45)
    $trkZoom.TabIndex                         = 0
    $trkZoom.Value                            = $Zoom
    $trkZoom.EndInit()

    $trkZoom.add_Scroll({
       $Zoom                 = $trkZoom.Value
       $MapType              = $cboMapDetails.Text
       $txtTrackbar.Text     = $trkZoom.Value
       $URL                  = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
       $webSample.Url        = $URL
       $webSample.Update()
      })

    $btnOk.Add_Click({
       $frmGoogleMapsZoom.Close()
       $frmGoogleMapsZoom.Dispose
      })

    $btnReset.Add_Click({
       $Zoom                 = 17
       $Maptype              = "roadmap"
       $trkZoom.Value        = $Zoom
       $cboMapDetails.Text   = $Maptype
       $txtTrackbar.Text     = $trkZoom.Value
       $URL                  = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
       $webSample.Url        = $URL
       $webSample.Update()
      })

     $cboMapDetails.Add_SelectedIndexChanged({
       $Zoom          = $trkZoom.Value
       $MapType       = ($cboMapDetails.SelectedItem)
       $URL           = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
       $webSample.Url = $URL
       $webSample.Update()
      })

    if($frmGoogleMapsZoom.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK)
     {
      $MapType       = $cboMapDetails.Text
      $Zoom          = $trkZoom.Value
      Return $Zoom, $MapType
     }
      else
     {
      Return 17,"roadmap"
     }
    
  }

  $Global:gblGoogleAPIKey = "Enter your Google API Key"
  if (Has-ValidGoogleAPIKey)
   {
    $Zoom, $Map             = Get-ZoomFactorAndMapLayout -Maptype hybrid -Zoom 5
    Write-Host "Zoom: $Zoom"
    Write-Host "Map:  $Map"
   }

Another improvement is checking the Google API key while starting the application. You can only proceed if the Google API key is valid or if not present at all. 

The third change is the date and time notation of the filenames: it is in year-month-day and the time is in the 24-hour notation. 

A small improvement in version 1.1:

The function 'UserDetails' kept me busy. There were still situations that the function could not handle. So, I added a list of requirements:

  1. Find the logged on user sid when there is one user logged on on a 'normal' Windows computer
  2. Find the logged on user sid when multiple users are logged on in an RDS / Citrix environment where there is no local admin.
  3. Find the logged on user sid when multiple users are logged on in an RDS / Citrix environment where the users are local admin.
  4. Find the logged on user sid when there is a SYSTEM account, and there is a user logged on on a 'normal' Windows computer. This occurs during a Software Center installation performed under the SYSTEM account.
  5. Find the logged on user sid when there is a SYSTEM account, and there is a user logged on on an RDS / Citrix environment.
  6. Take care of the PowerShell Language Mode.

While testing I found that the MainWindowHandle is not 0 for the user running the explorer process. That is true for all the above-mentioned situations.

You can check while running this command with elevated rights:

get-process -IncludeUserName | Select-Object -Property Username, Id, Name,MainWindowHandle | Where {($_.Name -eq "explorer" -or $_.Name -eq "pfwsmgr")} | Format-Table

The output will be something like this:

UserName             Id Name     MainWindowHandle
--------             -- ----     ----------------
DEMO\user1         1380 explorer                0
DEMO\adminuser2    6556 explorer           131134

So, you have to continue with PID ID 6556.

I use this method of finding the PID, and use that PID as a condition for the Get-WMIObject to search for the explorer or pfwsmgr process with that PID.

But remember that this code can only be run when the PowerShell Language Mode is set to 'FullLanguage'.

$PIDID = (get-process | Select-Object -Property Id, Name, MainWindowHandle | Where {(($_.Name -eq "explorer" -or $_.Name -eq "pfwsmgr") -and $($_.MainWindowHandle).ToInt32() -gt 0)}).Id

I assume that $($_.MainWindowHandle).ToInt32() -gt 0 is the culprit. 

 

NAME
    C:\tmp\PictureDetailsWithForms_v12\PictureDetailsWithForms_v12.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:\tmp\PictureDetailsWithForms_v12\PictureDetailsWithForms_v12.ps1 [-GoogleAPIKey <String>] [-LogPath <String>] 
    [-Directory <String>] [-LanguageOverride <String>] [-PhotoDetails <String>] [-DetailedLogging] 
    [-IncludeSubFolders] [-Silent] [-SaveAsPDF] [-ClearUserSettings] [-OverviewSupportedLanguages] [-NoUserSettings] 
    [-Zoomfactor <Int32>] [-Maptype <String>] [<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>
        
    -LanguageOverride <String>
        
    -PhotoDetails <String>
        
    -DetailedLogging [<SwitchParameter>]
        
    -IncludeSubFolders [<SwitchParameter>]
        
    -Silent [<SwitchParameter>]
        
    -SaveAsPDF [<SwitchParameter>]
        
    -ClearUserSettings [<SwitchParameter>]
        
    -OverviewSupportedLanguages [<SwitchParameter>]
        
    -NoUserSettings [<SwitchParameter>]
        
    -Zoomfactor <Int32>
        
    -Maptype <String>
        
    <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_v12.ps1"
    
    
    
    
    -------------------------- EXAMPLE 2 --------------------------
    
    PS C:\>Run an inventory on all the pictures in the current directory and log in C:\TMP
    
    ."PictureDetailsWithForms_v12.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_v12.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_v12.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.
    
    The language should be Greek.
    ."PictureDetailsWithForms_v12.ps1" -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -LanguageOverride el
    Use ."PictureDetailsWithForms_v12.ps1" -OverviewSupportedLanguages to find all the supported languages.
    
    
    
    
    -------------------------- EXAMPLE 6 --------------------------
    
    PS C:\>Clear all the stored settings before running the script.
    
    ."PictureDetailsWithForms_v12.ps1" -ClearUserSettings
    
    
    
    
    -------------------------- 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. Do not save the settings on exit.
    ."PictureDetailsWithForms_v12.ps1" -Directory C:\Pictures -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders 
    -ShowOnlyUniquePictures -NoUserSettings
    
    
    
    
REMARKS
    To see the examples, type: "get-help C:\tmp\PictureDetailsWithForms_v12\PictureDetailsWithForms_v12.ps1 -examples".
    For more information, type: "get-help C:\tmp\PictureDetailsWithForms_v12\PictureDetailsWithForms_v12.ps1 
    -detailed".
    For technical information, type: "get-help C:\tmp\PictureDetailsWithForms_v12\PictureDetailsWithForms_v12.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_v12.ps1"

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

.EXAMPLE
    Run an inventory on all the pictures in the current directory and detailed logging in C:\TMP
    ."PictureDetailsWithForms_v12.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_v12.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.
    The language should be Greek.
    ."PictureDetailsWithForms_v12.ps1" -GoogleAPIKey 1234567890-0987654321 -IncludeSubFolders -LanguageOverride el
    Use ."PictureDetailsWithForms_v12.ps1" -OverviewSupportedLanguages to find all the supported languages.

.EXAMPLE
    Clear all the stored settings before running the script. 
    ."PictureDetailsWithForms_v12.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_v12.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.

v0.2:
   * Some errors in the variable names have been corrected.
   * Tooltips
   * Multilangual: the following languages are supported: English, Dutch, French, Spanish and Indonesian. You can override with the LanguageOverride parameter.

v0.3:
   * There was a bug with the combination of -Logpath and -Silent. That has been solved.
   * The foldername is displayed in silent mode.

v0.4:
   * Solved: Add-Type : Cannot add type. The type name 'SAPIENTypes.ProgressBarOverlay' already exists.
   * LanguageOverride has a ValidateSet.

v0.5:
   * Introduction of the 'OverviewSupportedLanguages' switch.
   * Proxy support.
   * Google Maps information in PDF.
   * Picture has right orientation in PDF.
   * The parameter ShowOnlyUniquePictures is no longer used and has been removed.
   * Google Maps data has become a static map, otherwise itext7 cannot handle the output. It uses Java.

v1.0:
   * Improved error handling
   * The form cannot be resized anymore
   * Changed the date notation of the logfile to yyyy-MM-dd HH-mm-ss, for example '2022-11-14 22-23-09'.
   * Introduction Custom zoomfactor and custom maptype.
   * New parameters zoomfactor and maptype. 
   * Better validation on the Google API Key

v1.1:
   * The function 'UserDetails' has been modified.

v1.2:

#>

[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 = 'Override the language.')]
   [Parameter(Mandatory = $False, ParameterSetName = 'Default')]
   [ValidateScript({
     $JSONFile                    = $PSCommandPath -replace ".ps1",".json"  
     If (Test-Path($JSONFile))
      {
       $JSONObject                  = Get-Content -Path $JSONFile -Raw -Encoding UTF8 | ConvertFrom-Json    
       $Languages                   = @(($JSONObject | Get-Member -type NoteProperty).Name)
       $Languages                   = $Languages | Sort-Object
       If ($Languages -contains $_)
        {
         $True
        }
         else
        {
         $SupportedLanguages = ""
         ForEach ($Language in $Languages)
          {
           $LanguageName = ((([CultureInfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) | Where {$_.Name -like "$Language*"})[0]).DisplayName).Split(" ")[0]
           If ($SupportedLanguages.Length -eq 0)
            {
             $SupportedLanguages = "$Language ($LanguageName)"
            }
             else
            {
             $SupportedLanguages += ", $Language ($LanguageName)"
            } 
          }
         if($Languages.Count -gt 1)
          {
           $Lastcomma          = $SupportedLanguages.LastIndexOf(", ")
           $Firstpart          = $SupportedLanguages.Substring(0,$Lastcomma)
           $Lastpart           = $SupportedLanguages.SubString($Lastcomma+2,($SupportedLanguages.Length) - $Lastcomma -2)
           Clear-Host
           Throw "$_ is no supported language. The following $($Languages.Count) languages are supported: $Firstpart and $Lastpart."
          }
           else
          {
           Throw "$_ is no supported language. The following language is supported: $SupportedLanguages."
          }
        }
      }
       else
      {
       Write-Host "The JSON File '$JSONFile' is not found."
       Exit 99
      }
     })]
   [String]   $LanguageOverride,

   [Parameter(HelpMessage='Photo details.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [ValidateSet("full","basic","none")]
   [String]  $PhotoDetails,

   [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='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='Force a per machine install or uninstall.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Switch]   $OverviewSupportedLanguages,

   [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,

   [Parameter(HelpMessage='The zoomfactor, used in Google Maps. It is a number between 1 and 20.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [Int]     $Zoomfactor,

   [Parameter(HelpMessage='Do not use the saved user settings and do not save the user settings on exit.')]
   [Parameter(Mandatory=$False, ParameterSetName='Default')]
   [ValidateSet("roadmap","satellite","terrain","hybrid")]
   [String]  $Maptype
   
  )

  Clear-Host

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

  Function Add-EntryToLogFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-May-2020 / Modified 09-May-2022: Includes the function name
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-EntryToLogFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds a line to a log file

    #>

    Param
     (
      [Parameter(Mandatory=$True)]  [string] $Entry,
      [Parameter(Mandatory=$False)] [String] $FunctionName

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

  Function Check-WorkingInternetConnection
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       09-July-2020 / Modified 04-Oct-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Check-WorkingInternetConnection
    =============================================================================================================================================
    .SYNOPSIS

    Check for a working internet connection.

    (New-Object System.Net.NetworkInformation.Ping).SendPingAsync("www.google.com") works also when a proxy server is used.
    [Threading.Tasks.Task]::WaitAll($Status) will thrown an error if the host cannot be reached.
    Test-NetConnection and Test-Connection do not work if a proxy server is used. 

    #>

    Param
     (
      [String] $HostNameToCheck = "www.google.com"
     )

    $HostNameToCheck          = $HostNameToCheck.Replace("https://","").Replace("http://","")  # Remove https:// or http://
    $HostNameToCheck          = $HostNameToCheck.Replace("/","")                               # Remove a trailing / if needed.

    Try
     {
      $Status = (New-Object System.Net.NetworkInformation.Ping).SendPingAsync($HostNameToCheck)
      [Threading.Tasks.Task]::WaitAll($Status)
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Details:"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  - Result:        $($Status.Result.Status)" 
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  - Faulted:       $($Status.Isfaulted)"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  - Is Completed:  $($Status.IsCompleted)"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The host $HostNameToCheck can be reached. Thus there is a working internet connection."
      $Result = $True
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The host $HostNameToCheck cannot be reached. Thus there is no working internet connection."
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Error: $($Status.Exception)"
      $Result = $False
     }
    Return $Result
   }

  Function Get-Proxy
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       21-Sept-20222
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-Proxy
    =============================================================================================================================================
    .SYNOPSIS

    This function gets the proxy.

    If a proxy server is used, the returned server name is different from the server that is checked.

    #>

    $CheckServer      = "http://www.google.com/"

    $Result = $null
    $DefaultWebProxy  = [System.Net.WebRequest]::DefaultWebProxy.GetProxy([uri]($CheckServer)).AbsoluteUri
    $SystemWebProxy   = [System.Net.WebRequest]::GetSystemWebProxy().GetProxy([uri]$CheckServer).AbsoluteUri

    If ($DefaultWebProxy -ne $CheckServer)
     {
      $Result = $DefaultWebProxy
     }
      elseIf ($SystemWebProxy -ne $CheckServer)
     {
      $Result = $SystemWebProxy     
     }
    
    If ($Result)
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The proxy server '$Result' has been found and is used."
     }
      else
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "No proxy server has been found."
     }

    Return $Result
   }

  Function Display-MessageBox
   {

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

    This function displays a message box.

    The return value is:

    None (no result) 	0
    OK 	                1
    Cancel 	            2
    Yes 	            6
    No. 	            7

    #>

    param
     (
      [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()][String] $Text,
      [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()][String] $Title,
      [Parameter(Mandatory=$False)][Switch] $GUI,
      [Parameter(Mandatory=$False)][Switch] $Error,
      [Parameter(Mandatory=$False)][ValidateSet('Ok','AbortRetryIgnore','OkCancel','YesNoCancel','YesNo')][String] $Button = "Ok"
     )
         
    If ($GUI)
     {
      If ($Error)
       {
        $Return = [System.Windows.Forms.MessageBox]::Show($this,$Text.Trim(),$Title,$Button,"Error")
       }
        else
       {
        $Return = [System.Windows.Forms.MessageBox]::Show($this,$Text.Trim(),$Title,$Button,"Asterisk")
       }
     }
      else
     {
      Write-Host "$Text`n"
      Return 0
     }

     Return $($Return.value__)
   }

  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 Test-RegistryKeyValue
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-Dec-20
    Created by:       (C) Aaron Jensen 
                      https://stackoverflow.com/questions/5648931/test-if-registry-value-exists
    Organization:     Carbon Module
    Functionname:     Test-RegistryKeyValue
    =============================================================================================================================================
    .SYNOPSIS

    #>

    [CmdletBinding()]
    param 
     (
      [Parameter(Mandatory = $true)]
      [string]  $Path,
      [Parameter(Mandatory = $true)]
      [string]  $Name
     )

    If (-not (Test-Path -Path $Path -PathType Container))
     {
      return $false
     }

    $properties = Get-ItemProperty -Path $Path
    If (-not $properties)
     {
      return $false
     } 

    $member = Get-Member -InputObject $properties -Name $Name
    If ($member)
     {
      return $true
     }
      else
     {
      return $false
     }
   }

  Function UserDetails
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-Jan-21 / Modified on 14-Jan-21 / Modified on 22-Apr-22 / Modified on 01-May-2022 / Modified on 17-May-2022 /  
	                  Modified 21-Nov-2022
    Created by:       Willem-Jan Vroom
    Functionname:     UserDetails
    =============================================================================================================================================
    .SYNOPSIS

    This function returns 4 details of the Current Logged In Usser
    1. The username of the current logged in user
    2. User\Domain of the current logged in user
    3. User SID fo the User\Domain
    4. Account name that is using the script 

    Initially, the scriptAccount was found with the command 
    [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

    But that command throws up an error due to the PowerShell ConstrainedMode.

    Also, the output of whoami /user depends on the language. In the German language the output is different.
    The header information is in the German language, so the header should be read from the CSV file.
    That can be done with PSObject.Properties.Name

    C:\Users\Test>whoami /user

    BENUTZERINFORMATIONEN
     ---------------------

    Benutzername         SID
    ==================== ==============================================
    windows10wkg_02\test S-1-5-21-1753037473-1212035439-1379364385-1001

    On 21-Nov-2022 the function has been changed.

    There are 6 situations that should be handled:

    1. Find the logged on user sid when there is one user logged on on a 'normal' Windows computer
    2. Find the logged on user sid when multiple users are logged on in an RDS / Citrix environment where there is no local admin.
    3. Find the logged on user sid when multiple users are logged on in an RDS / Citrix environment where the users are local admin.
    4. Find the logged on user sid when there is a SYSTEM account and there is a user logged on on a 'normal' Windows computer. 
       This occurs during a Software Center installation performed under the SYSTEM account.
    5. Find the logged on user sid when there is a SYSTEM account and there is a user logged on on an RDS / Citrix environment.
    6. Take care of the PowerShell Language Mode.


    While testing I found that the MainWindowHandle is not 0 for the user that is running the explorer process. That is true for all the
    above mentioned situations.

    You can check while running this command with elevated rights:

    get-process -IncludeUserName | Select-Object -Property Username, Id, Name,MainWindowHandle | Where {($_.Name -eq "explorer" -or $_.Name -eq "pfwsmgr")} | Format-Table

    The output will be something like this:

    UserName             Id Name     MainWindowHandle
    --------             -- ----     ----------------
    DEMO\user1         1380 explorer                0
    DEMO\adminuser2    6556 explorer           131134

    So, you have to continue with PID ID 6556.

    So, I used the method of finding the PID, and use that PID as a condition for the Get-WMIObject to search for the explorer or pfwsmgr
    process with that PID.

    #>

  # =============================================================================================================================================
  # Find the current logged on user by checking the rights on the explore.exe (of pfwsmgr.exe with Ivanti Workspace Control) process.
  # If the language mode is FullLanguage then use MainWindowHandle. That is needed when this script is run from SYSTEM context. I assume that
  # the SYSTEM account is not effected by the PowerShell Language Mode.
  # If the language mode is not FullLanguage then use a check on username and the owner of the explorer or pfwsmgr process. 
  # =============================================================================================================================================

   if($Global:gblFullLanguage)
     {
      $PIDID              = (get-process | Select-Object -Property Id, Name, MainWindowHandle | Where {(($_.Name -eq "explorer" -or $_.Name -eq "pfwsmgr") -and $($_.MainWindowHandle).ToInt32() -gt 0)}).Id
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The process 'explorer' or 'pfwsmgr' has the PID $PIDID."
      $Explorer           = (Get-WMIObject -Query "Select * From Win32_Process Where (ProcessId=$PIDID)")
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The process with PID $PIDID has the name $($Explorer.Name)."

      $UserName           = $Explorer.GetOwner()
      $SID                = ($Explorer.GetOwnerSID()).SID
     }
      else
     {
      $Explorer      = @(Get-WMIObject -Query "Select * From Win32_Process Where (Name='explorer.exe' or Name='pfwsmgr.exe')")
      $EnvUSERNAME   = $Env:USERNAME

      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The process 'explorer.exe' or 'pfwsmgr.exe' is running $($Explorer.Count) times."

      For ($a=0; $a -le ($Explorer.Count); $a++)
       {
        $UserName      = ($Explorer[$a]).GetOwner()
        If ($($UserName.User) -eq $EnvUSERNAME)
         {
          $SID           = (($Explorer[$a]).GetOwnerSID()).SID
          Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "A valid SID '$SID' has been found for user '$EnvUSERNAME'." 
          Break
         }
       }
     }

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found domain:        $($UserName.Domain)."
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found username:      $($UserName.User)."
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found SID:           $SID."
        
    $UserAndDomain      = "$($Username.Domain )\$($Username.User)".ToUpper()
    $tmpScriptAccount   = (whoami /user /FO csv | convertfrom-csv)
    $TranslatedUserName = $tmpScriptAccount.PSObject.Properties.Name[0]
    $ScriptAccount      = $($tmpScriptAccount.$TranslatedUserName).ToUpper()

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found scriptaccount: $ScriptAccount"
    
    Return $($Username.User),$UserAndDomain,$SID,$ScriptAccount
   }

  Function Find-Language
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-Dec-20
    Created by:       Willem-Jan Vroom 
    Organisation:                      
    Functionname:     Find-Language
    =============================================================================================================================================
    .SYNOPSIS

    This function works the best, even running under system context.
    Get-UiCulture returns the UICulture details from the account that is used. So when the system account is used, incorrect
    details might be shown.

    #>

    [CmdletBinding()]
    Param
     (
      [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $CurrentUserSID
     )

    $Result           = "en-US"
    $ThisFunctionName = $MyInvocation.MyCommand.Name

    $RegKey = "REGISTRY::HKEY_USERS\$CurrentUserSID\Control Panel\Desktop"
    $Value  = "PreferredUILanguages"
    If (Test-RegistryKeyValue -Path $RegKey -Name $Value)
     {
      $Result = (get-itemproperty $RegKey | Select -ExpandProperty $Value).Split()[0]
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' exists. The data is '$Result'."
      Return $Result
     }
      else
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' does not exists. Skipping to machine settings."
     }

    $RegKey = "REGISTRY::HKEY_USERS\.DEFAULT\Control Panel\Desktop\MuiCached"
    $Value  = "MachinePreferredUILanguages"
    If (Test-RegistryKeyValue -Path $RegKey -Name $Value)
     {
      $Result = (get-itemproperty $RegKey | Select -ExpandProperty $Value).Split()[0]
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' exists. The data is '$Result'."
      Return $Result
     }

    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There was a problem reading the registry..."
    Return $Result
   }

  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 -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Created the registry key: $NewKey."
          }
        }

      }
     
     If ($Boolean)
      {
       If ($Value)
        { 
         New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType DWord -Value 1 -Force | Out-Null
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Created in the hive $($RegistryKey):"
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  Created ValueName: $ValueName"
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Type:      REG_DWORD"
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Value:     1" 
        }
         else
        {
         New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType DWord -Value 0 -Force | Out-Null
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Created in the hive $($RegistryKey):"
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  Created ValueName: $ValueName"
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Type:      REG_DWORD"
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Value:     0" 
        }
      }
       elseIf ($IsArray)
      {
       New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType String -Value $($Value -join ",") -Force | Out-Null
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Created in the hive $($RegistryKey):"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  Created ValueName: $ValueName"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Type:      REG_SZ"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Value:     $($Value -join ",")" 
      }
       else
      {
       New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType String -Value $Value -Force | Out-Null
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Created in the hive $($RegistryKey):"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  Created ValueName: $ValueName"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "          Type:      REG_SZ"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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 -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The registrykey '$RegistryKey' with valuename '$ValueName' exists."
        $PropertyType = (Get-Item $RegistryKey).GetValueKind($ValueName)
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Hive:          $RegistryKey"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  - ValueName: $ValueName"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "  - Type:      $PropertyType"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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:gblPhotoDetails -eq "basic")  {Set-Registry          -RegistryKey $RegistryKey  -ValueName "PhotoDetails"  -Value "basic"}          
      if ($Global:gblPhotoDetails -eq "full")   {Set-Registry          -RegistryKey $RegistryKey  -ValueName "PhotoDetails"  -Value "full"}          
      if ($Global:gblPhotoDetails -eq "none")   {Set-Registry          -RegistryKey $RegistryKey  -ValueName "PhotoDetails"  -Value "none"} 
      if ($Global:gblZoomfactor)                {Set-Registry          -RegistryKey $RegistryKey  -ValueName "Zoomfactor"    -Value $Global:gblZoomfactor}        
      if ($Global:gblMaptype)                   {Set-Registry          -RegistryKey $RegistryKey  -ValueName "Maptype"       -Value $($Global:gblMaptype.ToString())}        
     }
   }

  Function Has-ValidGoogleAPIKey
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       20-Nov-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Has-ValidGoogleAPIKey
    =============================================================================================================================================
    .SYNOPSIS

    This function checks the Google API Key.
    As the Google API Key is in a Global variable, there is no need to pass it as a parameter

    #>

    $URL         = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&size=100x100"
    $Result      = $null
    $ReturnValue = $True
    Try
     {
      If ($ProxyServer)
       {
        $Result  = Invoke-WebRequest -Uri $URL -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials -Method Get -UseBasicParsing
       }
        else
       {
        $Result  = Invoke-WebRequest -Uri $URL -Method Get -UseBasicParsing
       }
      }
       Catch
      {
       $ErrorMessage = "There is an error: $($_.Exception.Message)"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    * Error while creating the Google Maps map: '$ErrorMessage'."
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "      Check your Google API key, that might be incorrect."
      }

   if (-not $Result)
    {
     $ReturnValue = $False
    }

   Return $ReturnValue

   }

  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     = "",
      [String]  $Base64
     )

    If ($Global:gblPhotoDetails -eq "none")
     {
      $Record  = [ordered] @{"Column Picture"                           = "";
                             "Column Google Maps Map"                   = ""
                             }

      $Record."Column Picture"                   = $Picture
      $Record."Column Google Maps Map"           = $GoogleMapsMap
 
      $objRecord                          = New-Object PSObject -Property $Record
      $Global:gblarrHTMLTable               += $objRecord

      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "    Column Picture:           $Picture".Replace($Base64,"<Photo details in base64 format.>")
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "    Column Google Maps Map:   $GoogleMapsMap"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry " "
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "###############################################################################################################`n"
     }
      else
     {
      $Record  = [ordered] @{"Column Picture"                           = "";
                             "Column Photo Data"                        = "";
                             "Column Google Maps Map"                   = ""
                             }

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

      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "    Column Picture:           $Picture".Replace($Base64,"<Photo details in base64 format.>")
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "    Column Photo Data:        $PhotoData"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "    Column Google Maps Map:   $GoogleMapsMap"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry " "
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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,
      [Switch] $Full
     )

    $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 
    
    If ($Full) 
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "The parameter 'Full' has been applied."
      $EXIFNumbers = @(1..266)
     }
      else
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  "Only a limited set of properties."
      $EXIFNumbers = @(12,30,31,32,194,259,260,263,264)
     }

    ForEach($a in $EXIFNumbers) 
    { 
        If ($objFolder.getDetailsOf($objFolder, $a) -and $objFolder.getDetailsOf($objFile, $a))  
        { 
            Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  " -> Number:        $a"
            Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  " -> Description:   $($objFolder.getDetailsOf($objFolder, $a))"
            Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry  " -> Result:        $($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.

    0 or 1 = Horizontal (normal)
         2 = Mirror horizontal
         3 = Rotate 180
         4 = Mirror vertical
         5 = Mirror horizontal and rotate 270 CW
         6 = Rotate 90 CW
         7 = Mirror horizontal and rotate 90 CW
         8 = Rotate 270 CW
    
    These numbers come from https://exiftool.org/TagNames/EXIF.html

    #>

    Param
     (
      [String] $SourceFileName
     )

    $img                 = [System.Drawing.Image]::FromFile($SourceFileName)
    Try
     {
      $IMGOrientation      = ($img.PropertyItems | Where {($_.ID -eq 274)}).Value[0]
     }
      Catch
     {
      $IMGOrientation      = 0
     }

    $img.Dispose()
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Picture '$SourceFileName' has as orientation $IMGOrientation."
    Return $IMGOrientation 

  }  
  
  Function Get-GPSDetails
   {
 
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-July-2020, Modified on 19-Nov-2022
    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                 = [System.Drawing.Image]::FromFile($FileNameWithGPSDetails)
    $IMGPropertyItems    = $img.PropertyItems | Where {($_.ID -eq $ID)}
    [Double]$Deg         = 0
    [Double]$Min         = 0
    [Double]$Sec         = 0
    $GPSTable            = @()

    $ArrayIDs = @(0,4,8,12,16,20)

    ForEach ($a in $ArrayIDs)
     {
     
      $GPSRecord  = [ordered] @{"Number"                   = "";
                                "Value"                    = "";
                                "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."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 -FunctionName $($MyInvocation.MyCommand.Name) -Entry " "
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "     --- Overview of the EXIF GPS Data ---"
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "     --- ID $ID of $(Split-Path -Leaf $FileNameWithGPSDetails)"
    ForEach ($object in $GPSTable)
     {
      If ($object."Remark")
       {
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "       Number:                     $($object."Number")"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "       Value:                      $($object."Value")"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "       Decimal value (Unsigned)  : $($object."Decimal value (Unsigned)")"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "       Remark:                     $($object."Remark")"
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "       ---------------------------------------------------------------------"
       }
     }

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "         (Unsigned decimal value)  Deg1 / Deg2   ->   $Deg1 / $Deg2 = $Deg"
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "         (Unsigned decimal value)  Min1 / Min2   ->   $Min1 / $Min2 = $Min"
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "         (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     = [System.Drawing.Bitmap]::FromFile($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 -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    The picture $FileName has the following GPS coordinates:"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "       $GPSLat"        
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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 -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    The picture $FileName has the following decimal coordinates:"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "      $LatOrt"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "      $LonOrt"
    }
     else
    {
     
   # =============================================================================================================================================
   # Ohoh... No GPS information in this picture.
   # =============================================================================================================================================
     
     Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    The picture $FileName does not contain GPS information."
     $LatOrt = "1024"
     $LonOrt = "1024"
    }

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

  Function Make-Base64Image
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       28-Oct-2022, modified on 11-Nov-2022, modified on 19-Nov-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Make-Base64Image
    =============================================================================================================================================
    .SYNOPSIS

    This function converts and rotates a picture and convert it to a base64 image. 
    Only than Itext7 HTMLToPDF can handle the orientation. 

    On 19-Nov-2022 additional logging has been added, including the rotation.

    #>

    Param
     (
      [Parameter(Mandatory=$True)] [String] $SourceFileName,
      [Parameter(Mandatory=$False)][Int]    $Scale = 100,
      [Parameter(Mandatory=$True)] [Int]    $Orientation
     )
 
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Reading picture '$SourceFileName'."

    $img          = [System.Drawing.Image]::FromFile($SourceFileName)
    $ImgFormat    = [System.Drawing.Imaging.ImageFormat]::Jpeg
        
    $Size         = "$([int]($img.Width*($Scale/100))), $([int]($img.Height*($Scale/100)))"
    $img_new      = New-Object -TypeName System.Drawing.Bitmap($img, $Size)
    $MemoryStream = New-Object -TypeName System.IO.MemoryStream


    $RotateAction = $null

    If ($Orientation -eq 2) {$RotateAction = "RotateNoneFlipX" }
    If ($Orientation -eq 3) {$RotateAction = "RotateNoneFlipXY"}
    If ($Orientation -eq 4) {$RotateAction = "RotateNoneFlipY" }
    If ($Orientation -eq 5) {$RotateAction = "Rotate90FlipX"   }
    If ($Orientation -eq 6) {$RotateAction = "Rotate90FlipNone"}
    If ($Orientation -eq 7) {$RotateAction = "Rotate90FlipY"   }
    If ($Orientation -eq 8) {$RotateAction = "Rotate90FlipXY"  }

    if($RotateAction)
     {
      $img_new.RotateFlip([System.Drawing.RotateFlipType]::$RotateAction)
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The picture '$SourceFileName' has been rotated '$RotateAction'."
     }

    $img_new.Save($MemoryStream, $ImgFormat)
    $cImgBytes    = [Byte[]]($MemoryStream.ToArray())
    $sBase64      = [System.Convert]::ToBase64String($cImgBytes)
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The picture '$SourceFileName' has been converted to a Base64 image."
    $img_new.Dispose()

    Return $sBase64
       
   }

  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.

    Note 03-Nov-2022:

    The parameter '-UseBasicParsing' is needed to avoid this error:

    Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet
    Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. 

    #>

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

     Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Finding address for latitude $Latitude and longitude $Longitude."  

     If ($ProxyServer)
      {
       $Result  = Invoke-WebRequest -Uri "https://maps.googleapis.com/maps/api/geocode/json?latlng=$Latitude,$Longitude&key=$Global:gblGoogleAPIKey" -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials -Method Get -UseBasicParsing
      }
       else
      {
       $Result  = Invoke-WebRequest -Uri "https://maps.googleapis.com/maps/api/geocode/json?latlng=$Latitude,$Longitude&key=$Global:gblGoogleAPIKey" -Method Get -UseBasicParsing
      }

   # =============================================================================================================================================
   # This additional step is needed to convert 'strange' characters like ü, ß or ö.
   # Otherwise, these characters are shown as ??
   # =============================================================================================================================================
   
     If ($Result)
      {
       
       $Result = ($Result.Content | ConvertFrom-JSON)

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

     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 Get-ZoomFactorAndMapLayout
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-Nov-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-ZoomFactorAndMapLayout
    =============================================================================================================================================
    .SYNOPSIS

    This function gives the user the chance to set the:
     - zoomfactor for Google Maps pictures
     - mapinformation for Google Maps pictures

    #>

    Param
     (
      [Parameter(Mandatory=$false)] 
      [int]      $Zoom              = 17,

      [Parameter(Mandatory=$False)] 
      [ValidateSet("roadmap","satellite","terrain","hybrid")]
      [String[]] $MapType           = "roadmap"
     )

    [int]    $backup_Zoom    = $Zoom
    [string] $backup_Maptype = $MapType

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

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

  # =============================================================================================================================================
  # Declare variables
  # =============================================================================================================================================

    [int]      $MapsWidth         = 380
    [int]      $MapsHeight        = 380
               $URL               = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
    [String[]] $MapDetailsItems   = @("roadmap","satellite","terrain","hybrid")
               $FunctionName      = $($MyInvocation.MyCommand.Name)


  # =============================================================================================================================================
  # Make the form
  # =============================================================================================================================================
    
    $frmGoogleMapsZoom = New-Object 'System.Windows.Forms.Form'
    $webSample         = New-Object 'System.Windows.Forms.WebBrowser'
    $trkZoom           = New-Object 'System.Windows.Forms.TrackBar'
    $btnReset          = New-Object 'System.Windows.Forms.Button'
    $btnOk             = New-Object 'System.Windows.Forms.Button'
    $cboMapDetails     = New-Object 'System.Windows.Forms.ComboBox'
    $ToolTipsGUI       = New-Object 'System.Windows.Forms.ToolTip'
    $txtTrackbar       = New-Object 'System.Windows.Forms.TextBox'
        
    # frmGoogleMapsZoom
    #
    $frmGoogleMapsZoom.Controls.Add($btnReset)
    $frmGoogleMapsZoom.Controls.Add($btnOk)
    $frmGoogleMapsZoom.Controls.Add($webSample)
    $frmGoogleMapsZoom.Controls.Add($trkZoom)
    $frmGoogleMapsZoom.Controls.Add($cboMapDetails)
    $frmGoogleMapsZoom.Controls.Add($txtTrackbar)
    $frmGoogleMapsZoom.AutoScaleMode          = 'None'
    $frmGoogleMapsZoom.ClientSize             = New-Object System.Drawing.Size(457, 568)
    $frmGoogleMapsZoom.Name                   = 'frmGoogleMapsZoom'
    $frmGoogleMapsZoom.StartPosition          = 'CenterScreen'
    $frmGoogleMapsZoom.Text                   = 'Set Google API Zoom'
    
    # ToolTipsGUI
    # 
    $ToolTipsGUI.AutomaticDelay               = 500
    $ToolTipsGUI.AutoPopDelay                 = 5010
    $ToolTipsGUI.InitialDelay                 = 500
    $ToolTipsGUI.IsBalloon                    = $True
    $ToolTipsGUI.ReshowDelay                  = 100
    $ToolTipsGUI.Tag                          = 'Information'
    $ToolTipsGUI.ToolTipIcon                  = 'Info'
    $ToolTipsGUI.ToolTipTitle                 = $($PictureActions.$Language.AdditionalInformation_TT)

    # txtTrackbar
    #
    $txtTrackbar.Location                     = New-Object System.Drawing.Point(168, 450)
    $txtTrackbar.Name                         = 'txtTrackbar'
    $txtTrackbar.Size                         = New-Object System.Drawing.Size(26, 20)
    $txtTrackbar.TabIndex                     = 5
    $txtTrackbar.Readonly                     = $True
    $txtTrackbar.Text                         = $Zoom

    # cboMapDetails
    #
    $cboMapDetails.FormattingEnabled          = $True
    $cboMapDetails.Location                   = New-Object System.Drawing.Point(22, 452)
    $cboMapDetails.Name                       = 'cboMapDetails'
    $cboMapDetails.Size                       = New-Object System.Drawing.Size(121, 21)
    $cboMapDetails.TabIndex                   = 4
    $cboMapDetails.Dropdownstyle              = 'DropDownList'
    [void] $cboMapDetails.Items.AddRange($MapDetailsItems)
    $cboMapDetails.Text                       = $MapType

    # btnReset
    #
    $btnReset.Location                        = New-Object System.Drawing.Point(200,515)
    $btnReset.Name                            = 'btnReset'
    $btnReset.Size                            = New-Object System.Drawing.Size(100, 45)
    $btnReset.TabIndex                        = 3
    $btnReset.Text                            = $($PictureActions.$Language.btnReset)
    $btnReset.UseVisualStyleBackColor         = $True
    $ToolTipsGUI.SetToolTip($btnReset, $($PictureActions.$Language.btnReset_TT))

    # btnOk
    #
    $btnOk.Location                           = New-Object System.Drawing.Point(322, 515)
    $btnOk.Name                               = 'btnOk'
    $btnOk.Size                               = New-Object System.Drawing.Size(100, 45)
    $btnOk.TabIndex                           = 2
    $btnOk.Text                               = $($PictureActions.$Language.btnOk)
    $btnOk.UseVisualStyleBackColor            = $True
    $btnOk.DialogResult                       = 'OK'
    $ToolTipsGUI.SetToolTip($btnOk, $($PictureActions.$Language.btnOk_TT))
  
    # webSample
    #
    $webSample.AllowWebBrowserDrop            = $False
    $webSample.IsWebBrowserContextMenuEnabled = $False
    $webSample.Location                       = New-Object System.Drawing.Point(22, 37)
    $webSample.Margin                         = '10, 10, 10, 10'
    $webSample.MaximumSize                    = New-Object System.Drawing.Size(400, 400)
    $webSample.MinimumSize                    = New-Object System.Drawing.Size(400, 400)
    $webSample.Name                           = 'webSample'
    $webSample.ScrollBarsEnabled              = $False
    $webSample.Size                           = New-Object System.Drawing.Size(400, 400)
    $webSample.TabIndex                       = 1
    $webSample.Url                            = $URL
    $webSample.WebBrowserShortcutsEnabled     = $False
    $WebSample.Anchor                         = 'Top, Bottom, Left, Right'

    # trkZoom
    #
    $trkZoom.Location                         = New-Object System.Drawing.Point(200, 450)
    $trkZoom.Maximum                          = 20
    $trkZoom.Minimum                          = 1
    $trkZoom.Name                             = 'trkZoom'
    $trkZoom.Size                             = New-Object System.Drawing.Size(222, 45)
    $trkZoom.TabIndex                         = 0
    $trkZoom.Value                            = $Zoom
    $trkZoom.EndInit()

    $trkZoom.add_Scroll({
       $Zoom                 = $trkZoom.Value
       $MapType              = ($cboMapDetails.Text)
       $txtTrackbar.Text     = $trkZoom.Value
       $URL                  = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
       $webSample.Url        = $URL
       $webSample.Update()
       Add-EntryToLogFile -FunctionName $FunctionName -Entry "Zoom has been changed to $($trkZoom.Value)."
      })

    $btnOk.Add_Click({
       Add-EntryToLogFile -FunctionName $FunctionName -Entry "The button '$($PictureActions.$Language.btnOk)' has been clicked."
       $frmGoogleMapsZoom.Close()
       $frmGoogleMapsZoom.Dispose
      })

    $btnReset.Add_Click({
       Add-EntryToLogFile -FunctionName $FunctionName -Entry "The button '$($PictureActions.$Language.btnReset)' has been clicked."
       $Zoom                 = 17
       $Maptype              = "roadmap"
       $trkZoom.Value        = $Zoom
       $cboMapDetails.Text   = $Maptype
       $txtTrackbar.Text     = $trkZoom.Value
       $URL                  = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
       $webSample.Url        = $URL
       $webSample.Update()
       Add-EntryToLogFile -FunctionName $FunctionName -Entry "Zoom has been changed to '$($trkZoom.Value)' and the maptype has been changed to '$($cboMapDetails.Text)'."
      })

     $cboMapDetails.Add_SelectedIndexChanged({
       $Zoom                 = $trkZoom.Value
       $MapType              = ($cboMapDetails.SelectedItem)
       $URL                  = "https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&center=AmsterdamCS&markers=AmsterdamCS&zoom=$($Zoom)&maptype=$($MapType)&size=$($MapsWidth)x$($MapsHeight)"
       $webSample.Url        = $URL
       $webSample.Update()
       Add-EntryToLogFile -FunctionName $FunctionName -Entry "The maptype has been changed to '$MapType'."
      })

    if($frmGoogleMapsZoom.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK)
     {
      $MapType       = $cboMapDetails.Text
      $Zoom          = $trkZoom.Value

      Add-EntryToLogFile -FunctionName $FunctionName -Entry "Zoom:     $Zoom"
      Add-EntryToLogFile -FunctionName $FunctionName -Entry "Maptype:  $MapType"  
            
      Return $Zoom, $MapType
     }
      else
     {
      Add-EntryToLogFile -FunctionName $FunctionName -Entry "The cancel option is used."
      Add-EntryToLogFile -FunctionName $FunctionName -Entry "The backup zoom of $backup_Zoom and the backup maptype $backup_Maptype is returned."
      Return $backup_Zoom,$backup_Maptype
     }
   }

  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
     )     

  # =============================================================================================================================================
  # Define header and the various files that are used within the website. 
  # =============================================================================================================================================
  
    If ($SaveAsPDFFile)
     {
      $PictureWidth       = 350
      $MapsWidth          = 350
      $MapsHeight         = 350
     }
      else
     {
      $PictureWidth       = 600
      $MapsWidth          = 600
      $MapsHeight         = 600
     }
     
    $Header      = ""

    $CSS         = "<style type=$([char]34)text/css$([char]34)>`n"
    $CSS        += "img    {`n"
    $CSS        += "        height:            auto;`n"
    $CSS        += "       }`n`n"
    $CSS        += "table  {`n"
    $CSS        += "        font-size:         12px`n"
    $CSS        += "        table-layout:      fixed;`n"
    If ($SaveAsPDFFile)                      {$CSS        += "        border:            2px solid black;`n"}
    $CSS        += "        page-break-inside: avoid;`n"
    $CSS        += "        font-family:       Arial, Helvetica, sans-serif;`n"
    $CSS        += "       }`n`n"
    $CSS        += "td     {`n"
    $CSS        += "        padding:          4px;`n"
    $CSS        += "        margin:           10px;`n"
    $CSS        += "        max-width:        $($PictureWidth)px;`n"
    If ($SaveAsPDFFile)                      {$CSS        += "        border:            1px solid black;`n"}
    $CSS        += "        vertical-align:   top;`n"
    $CSS        += "        height:           auto;`n"
    $CSS        += "        overflow-wrap:    break-word;`n"
    $CSS        += "       }`n`n"
    $CSS        += "th     {`n"
    $CSS        += "        background:       #395870;`n"
    $CSS        += "        background:       linear-gradient(#49708f, #293f50);`n"
    If ($SaveAsPDFFile)                      {$CSS        += "        border:           1px solid black;`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"
    If ($SaveAsPDFFile) {$CSS        += "        border:            1px solid black;`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).FullName)
    
    $JPGFileNames  = $JPGFileNames | Sort-Object
 
  # =============================================================================================================================================
  # Go through all the jpg files.
  # =============================================================================================================================================

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

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Using zoomfactor: $($Global:gblZoomfactor)"
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Using maptype:    $($Global:gblMaptype)"

    ForEach ($JPGFullFileName in $JPGFileNames)
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Processing picture: $JPGFullFileName"
      $Orientation                      = Get-Orientation -SourceFileName $JPGFullFileName
      If ($GUI)
       {
        $Percentage                       = [Math]::Round(($valCounter / $TotalJPGFiles * 100),0)
        $pgbOverlay.Value                 = $Percentage
        $pgbOverlay.Step                  = $Step
        $pgbOverlay.PerformStep()
        $lblProcessing.Text               = $($PictureActions.$Language.lblProcessing) -replace "<nnn>",$($JPGFileName.Name) -replace "<zzz>",$valCounter -replace "<yyy>",$TotalJPGFiles
        $lblProcessing.Update()
        $pbPicturePreview.Image           = [System.Drawing.Bitmap]$JPGFullFileName
        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
       {
        $tmpLine2 = $($PictureActions.$Language.lblProcessing) -replace "<nnn>",$JPGFullFileName -replace "<zzz>",$valCounter -replace "<yyy>",$TotalJPGFiles
        Write-Progress -Id 1 -Activity $($($PictureActions.$Language.Activity) + ": $((Get-ChildItem -Path $JPGFullFileName).Directory)") -Status $tmpLine2 -PercentComplete $($valCounter / $TotalJPGFiles * 100)
       }
      
      $ClassText      = ""
      $tmpPhotoData   = ""
      If ($Global:gblPhotoDetails -eq "full")  {$tmpPhotoData   = (Get-PhotoMetaDataFromJPGFile -FileName $JPGFullFileName -Full)}
      if ($Global:gblPhotoDetails -eq "basic") {$tmpPhotoData   = (Get-PhotoMetaDataFromJPGFile -FileName $JPGFullFileName)}
  
    # ============================================================================================================================================= 
    # Use a base64 image. That will avoid all kind of orientation issues and issues while making a PDF with iText7 HTMLToPDF.
    # Also, you can use a .html file with base64 images everywhere as the information is stored in the .html file directly.
    # ============================================================================================================================================= 
      
      $ImgInBase64 = Make-Base64Image -SourceFileName $JPGFullFileName -Scale 25 -Orientation $Orientation
      $tmpPicture  = "<img src=$([char]34)data:image/jpeg;base64,$ImgInBase64$([char]34) width=$([char]34)$PictureWidth$([char]34)>`n"
      if ($Global:gblPhotoDetails -eq "none")
       {
        $tmpPicture += "<br>$($PictureActions.$Language.Filename) <b>$JPGFullFileName</b>"
       }
           
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    Image source: $tmpPicture.".Replace($ImgInBase64,"<Base64 Image information>")
      If ($InternetConnection)
       {
        If ($Global:gblGoogleAPIKey)
         {
          $Latitude,$Longitude,$GPSLatitude,$GPSLongitude = Get-EXIFDataFromJPGFile -FileName $JPGFullFileName
          If (-not($Latitude -eq "1024" -and $Longitude -eq "1024"))
           {
            Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    A valid latitude and longitude have been found in this picture."
            $Address     = Get-AddressBasedOnLatitudeAndLongitude -Latitude $Latitude -Longitude $Longitude 
            $tmpGoogleMapsMap = "<img src=$([char]34)https://maps.googleapis.com/maps/api/staticmap?key=$($Global:gblGoogleAPIKey)&markers=$Latitude,$Longitude&zoom=$($Global:gblZoomfactor)&maptype=$($Global:gblMaptype)&size=$($MapsWidth)x$($MapsHeight)$([char]34)>`n"
            $tmpGoogleMapsMap += "<p>Address: $Address</p>"
            $tmpGoogleMapsMap += "<p>GPS Coordinates:<br>"
            $tmpGoogleMapsMap += "  $GPSLatitude<br>"
            $tmpGoogleMapsMap += "  $GPSLongitude</p>"
           }
           else
           {
            $ErrorMessage = $($PictureActions.$Language.NoCoordinates)
            Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "    * Error: $ErrorMessage"
            $tmpGoogleMapsMap = $ErrorMessage
           }
         }
         else
         {
         $URL = "https://developers.google.com/maps/documentation/javascript/get-api-key" 
         Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "   * Error: No Google Maps API Provided. Specify one as a parameter or register one via 'zzz' and use it as a parameter".Replace("zzz",$URL)
         $tmpGoogleMapsMap  = "$($PictureActions.$Language.NoAPI_1)<br>"
         $tmpGoogleMapsMap += "$($PictureActions.$Language.NoAPI_2)<br>"
         $tmpGoogleMapsMap += "<ol>"
         $tmpGoogleMapsMap += "  <li>$($PictureActions.$Language.NoAPI_3)</li>"
         $tmpGoogleMapsMap += "  <li>$($PictureActions.$Language.NoAPI_4)</a>.</li>".Replace("<zzz>. ","<a href=$([char]34)$URL$([char]34)>")
         $tmpGoogleMapsMap += "</ol>"
         }    
       }
        else
       {
        $tmpGoogleMapsMap  = "$($PictureActions.$Language.NoAPI_5)<br>"
       }
    
      Add-EntryToHTMLTable -Picture $tmpPicture -PhotoData $tmpPhotoData -GoogleMapsMap $tmpGoogleMapsMap -Base64 $ImgInBase64
      $valCounter++
     }

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

    $Header += $CSS
    $Header += "</style>"
    $Header += ""

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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 -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The DLL file '$($DLLFile.FullName)' has been loaded successfully." 
           }
            Catch
           {
            $DLLFilesLoadedSuccessfully = $False
            Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Failure while loading the DLL file '$($DLLFile.FullName)': $($_.Exception.Message)."
           }
         }
       }
        else
       {
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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)
     {
      If ( -not ($Global:gblPhotoDetails -eq "none"))
       {
        $HTMLTable = ""
        ForEach ($Record in $Global:gblarrHTMLTable)
         {
          $HTMLTable   += $Record | ConvertTo-Html -As List -Fragment
          $HTMLTable   += "<br>"
         }
        $HTMLTable = $HTMLTable -replace ("<table>"   ,"<table max-width=700px>`n")
        $HTMLTable = $HTMLTable -replace ("<tr><td>"  ,"<tr><td max-width=300px>`n")
        $HTMLTable = $HTMLTable -replace ("</td><td>" ,"</td><td max-width=400px>`n")
       }
        else
       {
        $HTMLTable = $Global:gblarrHTMLTable | ConvertTo-Html -Fragment -PreContent "<h2>$($PictureActions.$Language.HTML_SubHeader)</h2>" -As Table
       }
     }
      else
     {
      $HTMLTable = $Global:gblarrHTMLTable | ConvertTo-Html -Fragment -PreContent "<h2>$($PictureActions.$Language.HTML_SubHeader)</h2>" -As Table
     }
   
    $HTMLTable = [System.Web.HttpUtility]::HtmlDecode($HTMLTable)

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

    $Title       = "<h1>$($PictureActions.$Language.HTML_Header)</h1><br>"

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

    $Report = $Report.Replace("Column Picture"         , $($PictureActions.$Language.Column_Picture))
    $Report = $Report.Replace("Column Photo Data"      , $($PictureActions.$Language.Column_PhotoData))
    $Report = $Report.Replace("Column Google Maps Map" , $($PictureActions.$Language.Column_GoogleMapsMap))
    $Report = $Report.Replace("Address"                , $($PictureActions.$Language.Address))
    $Report = $Report.Replace("GPS Coordinates"        , $($PictureActions.$Language.GPSCoordinates))
        
    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         = $($PictureActions.$Language.PDFFileCreated) -replace "<zzz>",$PDFFileToWrite
          Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $tmpLine
          If ($GUI)
           {
            Display-MessageBox -GUI -Text $tmpLine -Title $($PictureActions.$Language.Done)
           }
            else
           {
            Display-MessageBox -Text $tmpLine -Title $($PictureActions.$Language.Done)
           }

       # =============================================================================================================================================
       # Clean up the mess. :-)
       #   - Temp HTML file in the %TEMP% folder
       # =============================================================================================================================================

         Remove-Item -Path $HTMLFileToWrite -Force
         }
          else
         {
          $tmpLine = $($PictureActions.$Language.NoDLLFilesLoaded) -replace "<zzz>",$Global:gblDLLPath
          Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $tmpLine
          If ($GUI)
           {
            Display-MessageBox -GUI -Text $tmpLine -Title $($PictureActions.$Language.Error) -Error
           }
            else
           {
            Display-MessageBox -Text $tmpLine
           }
         }
       }
        else
       {
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -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 = $($PictureActions.$Language.HTMLPageCreated) -replace "zzz",$HTMLFileToWrite
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $tmpLine
      If ($GUI)
       {
        Display-MessageBox -GUI -Text $tmpLine -Title $($PictureActions.$Language.Done)
       }
        else
       {
        Display-MessageBox -Text $tmpLine -Title $($PictureActions.$Language.Done)
       }
     }
   }

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

# =============================================================================================================================================
# Show the supported languages
# =============================================================================================================================================

  If ($OverviewSupportedLanguages)
   {
    $PSFile                      = $PSCommandPath
    $JSONFile                    = $PSFile.Replace(".ps1",".json")
    If (Test-Path($JSONFile))
      {
       $JSONObject                  = Get-Content -Path $JSONFile -Raw -Encoding UTF8 | ConvertFrom-Json    
       $Languages                   = @(($JSONObject | Get-Member -type NoteProperty).Name)
       $Languages                   = $Languages | Sort-Object
       Write-Host "$($Languages.Count) Supported languages:"
       ForEach ($Language in $Languages)
        {
         $LanguageName = ((([CultureInfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) | Where {$_.Name -like "$Language*"})[0]).DisplayName).Split(" ")[0]
         Write-Host " - Use '$Language' for the $LanguageName language." 
        }
       
       Write-Host "Use '$([char](34))$PSFile$([char](34)) -LanguageOverride <language>' to override the language."
      }
       else
      {
       Write-Host "The file '$JSONFile' does not exists."
      }
    Exit 0
   }

# =============================================================================================================================================
# Declares the variables.
# =============================================================================================================================================
  
  $Global:gblarrHTMLTable             = @()
  $Global:gblDetailedLogging          = $DetailedLogging
  $Global:gblLogFile                  = $LogPath
  $Global:gblSilent                   = $Silent
  $Global:gblDLLPath                  = $(Split-Path $MyInvocation.MyCommand.Definition) + "\DLLFilesForItext7"
  $Global:gblDLLPathExists            = $False
  $MinimumWidth                       = 800
  $MinimumHeight                      = 800
  $RegistryKey                        = "HKCU:Software\VroomSoft\PictureDetails"
  $Message                            = ""
  $ApplicationVersion                 = "1.2"

  $PowerShellLanguageMode             = $ExecutionContext.SessionState.LanguageMode
  $Global:gblFullLanguage             = $PowerShellLanguageMode -eq "FullLanguage"
  $ErrorNumber                        = 0
  $ErrorText                          = ""
  $JSONFile                           = $(Split-Path $MyInvocation.MyCommand.Definition) + "\"+ $($MyInvocation.MyCommand.Name).Replace(".ps1",".json")
  
  If ($ClearUserSettings -and (Test-Path $RegistryKey))
   {
    Remove-Item -Path $RegistryKey -Force | Out-Null
   }
 
  If (-not($NoUserSettings))
   {  
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Reading the user settings from the registry: $RegistryKey."

    If ($GoogleAPIKey)
     {
      $Global:gblGoogleAPIKey           = $GoogleAPIKey
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter GoogleAPIKey was used from the command line. Thus the variable GoogleAPIKey is used with the value $($Global:gblGoogleAPIKey)."
     }
      else
     {
      $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey"
      If ($tmpValue1) {$Global:gblGoogleAPIKey                  = $tmpValue1} else {$Global:gblGoogleAPIKey           = $GoogleAPIKey}
     }

    If ($Directory)
     {
      $Global:gblDirectory           = $Directory
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter Directory was used from the command line. Thus the variable Directory is used with the value $($Global:gblDirectory)."
     }
      else
     {
      $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "Directory"
      If ($tmpValue1) {$Global:gblDirectory                     = $tmpValue1} else {$Global:gblDirectory              = $Directory}
     }

    If ($IncludeSubFolders)
      {
      $Global:gblIncludeSubFolders   = $IncludeSubFolders
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter IncludeSubFolders was used from the command line. Thus the variable Directory is used with the value $($Global:gblIncludeSubFolders)."
     }
      else      
     {
      $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "SubFolders"
      If ($tmpValue1) {[bool]$Global:gblIncludeSubFolders       = $tmpValue1} else {$Global:gblIncludeSubFolders      = $IncludeSubFolders}
     }
      
    If ($PhotoDetails)
     {
      $Global:gblPhotoDetails        = $PhotoDetails
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter PhotoDetails was used from the command line. Thus the variable PhotoDetails is used with the value $($Global:gblPhotoDetails)."
     }
      else      
     {
      $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "PhotoDetails"
      If ($tmpValue1) {$Global:gblPhotoDetails                  = $tmpValue1} else {$Global:gblPhotoDetails           = $PhotoDetails}
     }


    If ($Zoomfactor)
     {
      $Global:gblZoomfactor          = $Zoomfactor
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter zoomfactor was used from the command line. Thus the variable zoomfactor is used with the value $($Global:gblZoomfactor)."
     }
      else      
     {
      $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "Zoomfactor"
      If ($tmpValue1) {$Global:gblZoomfactor                    = $tmpValue1} else {$Global:Zoomfactor                = $Zoomfactor}
     }

    If ($Maptype)
     {
      $Global:gblMaptype             = $Maptype
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter Maptype was used from the command line. Thus the variable Maptype is used with the value $($Global:gblMaptype)."
     }
      else      
     {
      $tmpValue1 = Read-Registry -RegistryKey $RegistryKey -ValueName "Maptype"
      If ($tmpValue1) {$Global:gblMaptype                       = $tmpValue1} else {$Global:gblMaptype                = $Maptype}
     }
     
    If (-not($Global:gblPhotoDetails))    {$Global:gblPhotoDetails = "basic"}
    If (-not($Global:gblMaptype))         {$Global:gblMaptype      = "roadmap"}
    If (-not($Global:gblZoomfactor))      {$Global:gblZoomfactor   = [int]17}
   }
    else
   {
    $Global:gblShowOnlyUniquePictures = $ShowOnlyUniquePictures
    $Global:gblIncludeSubFolders      = $IncludeSubFolders
    $Global:gblGoogleAPIKey           = $GoogleAPIKey
    $Global:gblDirectory              = $Directory
    $Global:gblPhotoDetails           = $PhotoDetails
    $Global:gblZoomfactor             = $Zoomfactor
    $Global:gblMaptype                = $Maptype
   }
  
  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
   } 

# =============================================================================================================================================
# Define the HTML File.
# It is saved as yyyy-MM-dd HH-mm-ss, for example 2022-11-14 22-23-09
# =============================================================================================================================================
 
  $LastPartOfHTMLFile = " (" + (Get-Date -format "yyyy-MM-dd HH-mm-ss").ToString() + ").html"
  $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
   }

  If ($Message)
   {
    If ($bolError)
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "$Message"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "This script will now quit."
      Write-Error $Message -Category CloseError
      Exit 3
     }
      else
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "[Warning]: $Message"
     }
   }

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

  $ThisFunctionName                              = "[Main - Arguments]"
  If ($Global:gblFullLanguage)
   {
    $Global:TableWithParameters  = @()
    $RecordWithParameters = [ordered] @{"Key"     = "";
                                        "Value"   = ""}
  
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** Parameters part           *****"
  
    ForEach($boundparam in $PSBoundParameters.GetEnumerator()) 
     {
      $tmpValue                     = $($boundparam.Value)
      $Value                        = ""
      If ($tmpValue -is [array])
       {
        ForEach ($object in $tmpValue)
         {
          If (-not($value))
           {
            $Value = $object
           }
            else
           {
            $Value +=",$($object)"
           }
         }
       }
        else
       {
        $Value = $tmpValue
       }
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Key: $($boundparam.Key) Value: $Value" 
     }
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** End Parameters part       *****`r`n" 
   }
    else
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The parameters are not written to the logfile due to PowerShell ConstrainedMode."
   }

# =============================================================================================================================================
# Write the logged in user details to the log file. 
# =============================================================================================================================================
  
  $OnlyUserName,               `
  $LoggedOnUserInDomainFormat, `
  $UseridSID,                  `
  $InstallAccount                 = UserDetails

  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "***** User details part         *****" 
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Logged on user:       $LoggedOnUserInDomainFormat"
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Logged on user (SID): $UseridSID"
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Installation account: $InstallAccount"
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "***** End User details part     *****`r`n"

# =============================================================================================================================================
# Read the JSON file with the translations.
# If the JSON file does not contain the detected language, then fallback to English.
# =============================================================================================================================================

  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "***** Language part             *****"
  If ($LanguageOverride)
   {
    $Language = $LanguageOverride
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The parameter -LanguageOverride is used. The language is '$Language'."
   }
    else
   {
    $Language = (Find-Language -CurrentUserSID $UseridSID).SubString(0, 2)
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The computer is in the $Language language."
   }

  if (Test-Path $JSONFile)
   {
    $PictureActions = Get-Content $JSONFile -Encoding UTF8 | ConvertFrom-Json

    If (-not ($PictureActions.$Language))
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The language '$Language' is not found in the json file '$JSONFile'."
      $Language = "en"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Falling back to the default language '$Language'."
     }

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The language '$Language' is used."
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "***** End language part         *****`r`n" 

  # =============================================================================================================================================
  # Define the error number and messages
  # =============================================================================================================================================

    $Error01  = $($PictureActions.$Language.Error001)
    $Error02  = $($PictureActions.$Language.Error002).Replace("<zzz>",$PowerShellLanguageMode)
    $Error32  = $($PictureActions.$Language.Error032).Replace("<","").Replace(">","")
    $Error64  = $($PictureActions.$Language.Error064)
    $Error128 = $($PictureActions.$Language.Error128).Replace("<zzz>",$($Global:gblGoogleAPIKey))
   }
    else
   {
    $Error16  = "The '$JSONFile' file with the translations does not exists."
    $ErrorNumber += 16
   }

# =============================================================================================================================================
# End reading the JSON file with the translations.
# =============================================================================================================================================

# =============================================================================================================================================
#  Do some error handling:
#    - Check if there is a working internet connection.
#    - Powershell language mode shoudl be FullLanguage.
# =============================================================================================================================================
  
  $InternetConnection  = Check-WorkingInternetConnection -HostNameToCheck www.bing.com

  If (-not($InternetConnection))                                                        {$ErrorNumber += 1}
  If (-not($Global:gblFullLanguage))                                                    {$ErrorNumber += 2}
  If (-not $Global:gblDirectory -and $Silent)                                           {$ErrorNumber += 32}
  If ($Global:Zoomfactor -and ($Global:Zoomfactor -lt 1 -or $Global:Zoomfactor -gt 20)) {$ErrorNumber += 64}
  
  If ($InternetConnection)                                                              {$ProxyServer = Get-Proxy}
  
  If ($InternetConnection -and $Global:gblGoogleAPIKey)                                 {If (-not (Has-ValidGoogleAPIKey)){$ErrorNumber += 128}}

  If ($Global:gblFullLanguage -and (-not $Global:gblSilent))
   {

  # =============================================================================================================================================
  # 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     
    
    [void][reflection.assembly]::Load('System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
    [void][reflection.assembly]::Load('System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')

    $DetectedWidth  = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Width
    $DetectedHeight = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Height
    
    If ($DetectedWidth -le $MinimumWidth)
     {
      $Error04  = $($PictureActions.$Language.Error004).Replace("<zzz>",$DetectedWidth).replace("<yyy>", $MinimumWidth).Replace("<","").Replace(">","")
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $Error04
      $ErrorNumber += 4
     }
    If ($DetectedHeight -le $MinimumHeight) 
     {
      $Error08  = $($PictureActions.$Language.Error008).Replace("<zzz>",$DetectedHeight).Replace("<yyy>",$MinimumHeight).Replace("<","").Replace(">","")
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $Error08
      $ErrorNumber += 8
     }
   }

# =============================================================================================================================================
# Error handling.
# =============================================================================================================================================

  If (($ErrorNumber -band 1) -eq 1)      {If ($ErrorText) {$ErrorText += "`n$Error01"}  else {$ErrorText = $Error01}}
  If (($ErrorNumber -band 2) -eq 2)      {If ($ErrorText) {$ErrorText += "`n$Error02"}  else {$ErrorText = $Error02}}
  If (($ErrorNumber -band 4) -eq 4)      {If ($ErrorText) {$ErrorText += "`n$Error04"}  else {$ErrorText = $Error04}}
  If (($ErrorNumber -band 8) -eq 8)      {If ($ErrorText) {$ErrorText += "`n$Error08"}  else {$ErrorText = $Error08}}
  If (($ErrorNumber -band 16) -eq 16)    {If ($ErrorText) {$ErrorText += "`n$Error16"}  else {$ErrorText = $Error16}}
  If (($ErrorNumber -band 32) -eq 32)    {If ($ErrorText) {$ErrorText += "`n$Error32"}  else {$ErrorText = $Error32}}
  If (($ErrorNumber -band 64) -eq 64)    {If ($ErrorText) {$ErrorText += "`n$Error64"}  else {$ErrorText = $Error64}}
  If (($ErrorNumber -band 128) -eq 128)  {If ($ErrorText) {$ErrorText += "`n$Error128"} else {$ErrorText = $Error128}}
  
  If ($ErrorNumber -gt 0)
   {
    $GUI = $True
    if ($Silent -or -not $Global:gblFullLanguage) {$GUI = $False}
    $DummyValue = Display-MessageBox -Text $ErrorText -GUI:$GUI -Error -Title "Error."
    Exit $ErrorNumber
   }

# =============================================================================================================================================
# End Error handling.
# =============================================================================================================================================
  
# =============================================================================================================================================
# Make the form header.
# =============================================================================================================================================

  $FormHeader                         = $($PictureActions.$Language.frmTestPicture) + " ($ApplicationVersion)"

# =============================================================================================================================================
# End making the form header.
# =============================================================================================================================================

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

# =============================================================================================================================================
# 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
    #----------------------------------------------
    try{
        [SAPIENTypes.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
    }
    #endregion Define SAPIEN Types
  
  # =============================================================================================================================================
  # Forms block (main form)
  # =============================================================================================================================================

    [System.Windows.Forms.Application]::EnableVisualStyles()
    $frmTestPicture                              = New-Object 'System.Windows.Forms.Form'
    $grpPhotoDetails                             = New-Object 'System.Windows.Forms.GroupBox'
    $rdNone                                      = New-Object 'System.Windows.Forms.RadioButton'
    $rdBasic                                     = New-Object 'System.Windows.Forms.RadioButton'
    $rdFull                                      = New-Object 'System.Windows.Forms.RadioButton'
    $txtOutputDir                                = New-Object 'System.Windows.Forms.TextBox'
    $lblOutputDir                                = New-Object 'System.Windows.Forms.Label'
    $btnSaveASPDF                                = New-Object 'System.Windows.Forms.Button'
    $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'
    $ToolTips                                    = New-Object 'System.Windows.Forms.ToolTip'
    $btnSetGoogleMapsInfo                        = New-Object 'System.Windows.Forms.Button'
    $txtMapType                                  = New-Object 'System.Windows.Forms.TextBox'
    $lblMapType                                  = New-Object 'System.Windows.Forms.Label'
    $txtZoomFactor                               = New-Object 'System.Windows.Forms.TextBox'
    $lblZoomFactor                               = New-Object 'System.Windows.Forms.Label'
    $btnReadAPIFromCb                     = New-Object 'System.Windows.Forms.Button'

    # frmTestPicture
    #
    $frmTestPicture.Controls.Add($grpPhotoDetails)
    $frmTestPicture.Controls.Add($txtOutputDir)
    $frmTestPicture.Controls.Add($lblOutputDir)
    $frmTestPicture.Controls.Add($btnSaveASPDF)
    $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.Controls.Add($btnSetGoogleMapsInfo)
    $frmTestPicture.Controls.Add($txtMapType)
    $frmTestPicture.Controls.Add($lblMapType)
    $frmTestPicture.Controls.Add($txtZoomFactor)
    $frmTestPicture.Controls.Add($lblZoomFactor)
    $frmTestPicture.Controls.Add($btnReadAPIFromCb)
    $frmTestPicture.AutoScaleDimensions          = New-Object System.Drawing.SizeF(6, 13)
    $frmTestPicture.AutoScaleMode                = 'Font'
    $frmTestPicture.ClientSize                   = New-Object System.Drawing.Size(784, 753)
    $frmTestPicture.Name                         = 'frmTestPicture'
    $frmTestPicture.StartPosition                = 'CenterScreen'
    $frmTestPicture.Text                         = $FormHeader
    $frmTestPicture.FormBorderStyle              = 'Fixed3D'
     
    # txtMapType
    #
    $txtMapType.Location                         = New-Object System.Drawing.Point(144, 637)
    $txtMapType.Name                             = 'txtMapType'
    $txtMapType.Size                             = New-Object System.Drawing.Size(100, 20)
    $txtMapType.TabIndex                         = 25
    $txtMapType.Text                             = $Global:gblMaptype
    
    # lblMapType
    #
    $lblMapType.AutoSize                         = $True
    $lblMapType.Location                         = New-Object System.Drawing.Point(15, 637)
    $lblMapType.Name                             = 'lblMapType'
    $lblMapType.Size                             = New-Object System.Drawing.Size(117, 13)
    $lblMapType.TabIndex                         = 24
    $lblMapType.Text                             = "$($PictureActions.$Language.lblMapType):"
    
    # txtZoomFactor
    #
    $txtZoomFactor.Location                      = New-Object System.Drawing.Point(144, 605)
    $txtZoomFactor.Name                          = 'txtZoomFactor'
    $txtZoomFactor.Size                          = New-Object System.Drawing.Size(100, 20)
    $txtZoomFactor.TabIndex                      = 23
    $txtZoomFactor.Text                          = $Global:gblZoomfactor
    
    # lblZoomFactor
    #
    $lblZoomFactor.AutoSize                      = $True
    $lblZoomFactor.Location                      = New-Object System.Drawing.Point(15, 608)
    $lblZoomFactor.Name                          = 'lblZoomFactor'
    $lblZoomFactor.Size                          = New-Object System.Drawing.Size(130, 13)
    $lblZoomFactor.TabIndex                      = 22
    $lblZoomFactor.Text                          = "$($PictureActions.$Language.lblZoomFactor):"
          
    # ToolTips
    #
    $ToolTips.AutomaticDelay                     = 500
    $ToolTips.AutoPopDelay                       = 5010
    $ToolTips.InitialDelay                       = 500
    $ToolTips.IsBalloon                          = $True
    $ToolTips.ReshowDelay                        = 100
    $ToolTips.Tag                                = 'Information'
    $ToolTips.ToolTipIcon                        = 'Info'
    $ToolTips.ToolTipTitle                       = $($PictureActions.$Language.AdditionalInformation_TT)

    # grpPhotoDetails
    #
    $grpPhotoDetails.Controls.Add($rdNone)
    $grpPhotoDetails.Controls.Add($rdBasic)
    $grpPhotoDetails.Controls.Add($rdFull)
    $grpPhotoDetails.Location = New-Object System.Drawing.Point(526, 576)
    $grpPhotoDetails.Name = 'grpPhotoDetails'
    $grpPhotoDetails.Size = New-Object System.Drawing.Size(246, 109)
    $grpPhotoDetails.TabIndex = 20
    $grpPhotoDetails.TabStop = $False
    $grpPhotoDetails.Text = $($PictureActions.$Language.grpPhotoDetails)

    # rdNone
    #
    $rdNone.Location = New-Object System.Drawing.Point(7, 80)
    $rdNone.Name = 'rdNone'
    $rdNone.Size = New-Object System.Drawing.Size(230, 25)
    $rdNone.TabIndex = 2
    $rdNone.TabStop = $True
    $rdNone.Text = $($PictureActions.$Language.rdNone)
    $rdNone.UseVisualStyleBackColor = $True

    # rdBasic
    #
    $rdBasic.Location = New-Object System.Drawing.Point(7, 49)
    $rdBasic.Name = 'rdBasic'
    $rdBasic.Size = New-Object System.Drawing.Size(230, 25)
    $rdBasic.TabIndex = 1
    $rdBasic.TabStop = $True
    $rdBasic.Text = $($PictureActions.$Language.rdBasic)
    $rdBasic.UseVisualStyleBackColor = $True

    # rdFull
    #
    $rdFull.Location = New-Object System.Drawing.Point(7, 18)
    $rdFull.Name = 'rdFull'
    $rdFull.Size = New-Object System.Drawing.Size(230, 25)
    $rdFull.TabIndex = 0
    $rdFull.TabStop = $True
    $rdFull.Text = $($PictureActions.$Language.rdFull)
    $rdFull.UseVisualStyleBackColor = $True

    # txtOutputDir
    #
    $txtOutputDir.Location = New-Object System.Drawing.Point(144, 539)
    $txtOutputDir.Name = 'txtOutputDir'
    $txtOutputDir.Size = New-Object System.Drawing.Size(400, 20)
    $txtOutputDir.TabIndex = 19

    # lblOutputDir
    #
    $lblOutputDir.AutoSize = $True
    $lblOutputDir.Location = New-Object System.Drawing.Point(15, 540)
    $lblOutputDir.Name = 'lblOutputDir'
    $lblOutputDir.Size = New-Object System.Drawing.Size(85, 13)
    $lblOutputDir.TabIndex = 18
    $lblOutputDir.Text = $($PictureActions.$Language.lblOutputDir)
 
    # btnReadAPIFromCb
    #
    $btnReadAPIFromCb.Location            = New-Object System.Drawing.Point(550, 486)
    $btnReadAPIFromCb.Name                = 'btnReadAPIFromCb'
    $btnReadAPIFromCb.Size                = New-Object System.Drawing.Size(222, 23)
    $btnReadAPIFromCb.TabIndex            = 26
    $btnReadAPIFromCb.Text                = $($PictureActions.$Language.btnReadAPIFromCb)
    $ToolTips.SetToolTip($btnReadAPIFromCb, $($PictureActions.$Language.btnReadAPIFromCb_TT))
    $btnReadAPIFromCb.Add_Click({
    $Global:gblGoogleAPIKey = Get-Clipboard -Format Text
    if (Has-ValidGoogleAPIKey)
     {
      $btnSetGoogleMapsInfo.Visible              = $True
      $txtMapType.Visible                        = $True
      $lblMapType.Visible                        = $True
      $txtZoomFactor.Visible                     = $True
      $lblZoomFactor.Visible                     = $True
      $txtGoogleAPIKey.Text                      = $Global:gblGoogleAPIKey
     }
      else
     {
      $ErrorText = $($PictureActions.$Language.NoValidAPIKey).Replace("<zzz>",$Global:gblGoogleAPIKey)
      $null = Display-MessageBox -Text $ErrorText -Title $($PictureActions.$Language.Error) -GUI -Button Ok -Error
     }
    })
    
    # btnSaveAsHTMLPage
    #
    $btnSaveAsHTMLPage.AccessibleDescription     = ''
    $btnSaveAsHTMLPage.Location                  = New-Object System.Drawing.Point(526, 691)
    $btnSaveAsHTMLPage.Name                      = 'btnSaveAsHTMLPage'
    $btnSaveAsHTMLPage.Size                      = New-Object System.Drawing.Size(120, 50)
    $btnSaveAsHTMLPage.TabIndex                  = 8
    $btnSaveAsHTMLPage.Text                      = $($PictureActions.$Language.btnSaveAsHTMLPage)
    $btnSaveAsHTMLPage.UseVisualStyleBackColor   = $True
    $ToolTips.SetToolTip($btnSaveAsHTMLPage, $($PictureActions.$Language.btnSaveAsHTMLPage_TT))
    $btnSaveAsHTMLPage.add_Click({Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The button '$($btnSaveAsHTMLPage.Text)' has been clicked."
                                  $Global:gblShowOnlyUniquePictures = $False
                                  $Global:gblIncludeSubFolders      = $False
                                  $Global:gblGoogleAPIKey           = $txtGoogleAPIKey.Text
                                  $Global:gblDirectory              = $txtDirectory.Text
                                  If ($rdNone.Checked)                    {$Global:gblPhotoDetails = "none"}
                                  If ($rdBasic.Checked)                   {$Global:gblPhotoDetails = "basic"}
                                  If ($rdFull.Checked)                    {$Global:gblPhotoDetails = "full"}
                                  If ($chkUniquePicturesOnly.Checked)     {$Global:gblShowOnlyUniquePictures = $True}
                                  If ($chkIncludeSubfolders.Checked)      {$Global:gblIncludeSubFolders      = $True}
                                  
                                  If (Test-Path $Global:gblDirectory)
                                   {
                                    Process-AllthePictures -HTMLFileToWrite $HTMLFile -GUI
                                    If (-not($NoUserSettings))
                                     {
                                      ReadAndSetSettings -Set -RegistryKey $RegistryKey
                                     }
                                    $frmTestPicture.Close()
                                    $frmTestPicture.Dispose
                                   }
                                    else
                                   {
                                    $tmpLine = $($PictureActions.$Language.FolderNotExists).Replace("<zzz>",$Global:gblDirectory)
                                    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $tmpLine
                                    Display-MessageBox -Text $tmpLine -Title $($PictureActions.$Language.Error) -GUI -Button Ok -Error
                                   }
                                 })

    # btnSaveASPDF
    #
    $btnSaveASPDF.Location                       = New-Object System.Drawing.Point(400, 691)
    $btnSaveASPDF.Name                           = 'btnSaveASPDF'
    $btnSaveASPDF.Size                           = New-Object System.Drawing.Size(120, 50)
    $btnSaveASPDF.TabIndex                       = 15
    $btnSaveASPDF.Text                           = $($PictureActions.$Language.btnSaveASPDF)
    $btnSaveASPDF.UseVisualStyleBackColor        = $True
    $ToolTips.SetToolTip($btnSaveASPDF, $($PictureActions.$Language.btnSaveASPDF_TT))
    $btnSaveASPDF.add_Click({Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The button '$($btnSaveASPDF.Text)' has been clicked."
                             $Global:gblShowOnlyUniquePictures = $False
                             $Global:gblIncludeSubFolders      = $False
                             $Global:gblGoogleAPIKey           = $txtGoogleAPIKey.Text
                             $Global:gblDirectory              = $txtDirectory.Text
                             If ($rdNone.Checked)                    {$Global:gblPhotoDetails = "none"}
                             If ($rdBasic.Checked)                   {$Global:gblPhotoDetails = "basic"}
                             If ($rdFull.Checked)                    {$Global:gblPhotoDetails = "full"}
                             If ($chkUniquePicturesOnly.Checked)     {$Global:gblShowOnlyUniquePictures = $True}
                             If ($chkIncludeSubfolders.Checked)      {$Global:gblIncludeSubFolders      = $True}
                             If (Test-Path $Global:gblDirectory)
                              {
                               Process-AllthePictures -HTMLFileToWrite $HTMLFile -GUI -SaveAsPDF
                               If (-not($NoUserSettings))
                                {
                                 ReadAndSetSettings -Set -RegistryKey $RegistryKey
                                }
                               $frmTestPicture.Close()
                               $frmTestPicture.Dispose
                              }
                               else
                              {
                               $tmpLine = $($PictureActions.$Language.FolderNotExists).Replace("<zzz>",$Global:gblDirectory)
                               Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $tmpLine
                               Display-MessageBox -Text $tmpLine -Title $($PictureActions.$Language.Error) -GUI -Button Ok -Error
                              }
                            })

    # btnBrowse 
    # 
    $btnBrowse.Location                          = New-Object System.Drawing.Point(550, 510)
    $btnBrowse.Name                              = 'btnBrowse'
    $btnBrowse.Size                              = New-Object System.Drawing.Size(222, 23)
    $btnBrowse.TabIndex                          = 13
    $btnBrowse.Text                              = $($PictureActions.$Language.btnBrowse)
    $btnBrowse.UseVisualStyleBackColor           = $True
    $ToolTips.SetToolTip($btnBrowse, $($PictureActions.$Language.btnBrowse_TT))
    $btnBrowse.add_Click({
                          $txtDirectory.Text = Get-FolderFromButton -InitialDirectory $Global:gblDirectory -Description "Directory with the pictures"
                          $Global:gblDirectory = $txtDirectory.Text
                          $txtDirectory.SelectionStart = $txtDirectory.Text.Length
                          $txtDirectory.ScrollToCaret()
                          })


    # btnSetGoogleMapsInfo
    #
    $btnSetGoogleMapsInfo.Location               = New-Object System.Drawing.Point(264, 605)
    $btnSetGoogleMapsInfo.Name                   = 'btnSetGoogleMapsInfo'
    $btnSetGoogleMapsInfo.Size                   = New-Object System.Drawing.Size(121, 52)
    $btnSetGoogleMapsInfo.TabIndex               = 21
    $btnSetGoogleMapsInfo.Text                   = $($PictureActions.$Language.btnSetGoogleMapsInfo)
    $btnSetGoogleMapsInfo.UseVisualStyleBackColor = $True
    $ToolTips.SetToolTip($btnSetGoogleMapsInfo, $($PictureActions.$Language.btnSetGoogleMapsInfo_TT))
    $btnSetGoogleMapsInfo.add_click({Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The button '$($btnSetGoogleMapsInfo.Text)' has been clicked."
                          $Global:gblZoomfactor, $Global:gblMaptype = Get-ZoomFactorAndMapLayout -Zoom $($Global:gblZoomfactor) -MapType $($Global:gblMaptype) 
                          $txtMapType.Text                          = $Global:gblMaptype
                          $txtZoomFactor.Text                       = $Global:gblZoomfactor

                         })

    # chkIncludeSubfolders
    #
    $chkIncludeSubfolders.Location               = New-Object System.Drawing.Point(15, 576)
    $chkIncludeSubfolders.Name                   = 'chkIncludeSubfolders'
    $chkIncludeSubfolders.Size                   = New-Object System.Drawing.Size(156, 25)
    $chkIncludeSubfolders.TabIndex               = 12
    $chkIncludeSubfolders.Text                   = $($PictureActions.$Language.chkIncludeSubfolders)
    $chkIncludeSubfolders.UseVisualStyleBackColor= $True
    $chkIncludeSubfolders.Checked                = $Global:gblIncludeSubFolders
    $ToolTips.SetToolTip($chkIncludeSubfolders, $($PictureActions.$Language.chkIncludeSubfolders_TT))

    # txtDirectory
    #
    $txtDirectory.Location                       = New-Object System.Drawing.Point(144, 513)
    $txtDirectory.Name                           = 'txtDirectory'
    $txtDirectory.Size                           = New-Object System.Drawing.Size(400, 20)
    $txtDirectory.TabIndex                       = 11
    $txtDirectory.Text                           = $Global:gblDirectory
    $txtDirectory.SelectionStart                 = $txtDirectory.Text.Length
    $txtDirectory.ScrollToCaret()

    # lblDirectory
    # 
    $lblDirectory.AutoSize                       = $True
    $lblDirectory.Location                       = New-Object System.Drawing.Point(15, 515)
    $lblDirectory.Name                           = 'lblDirectory'
    $lblDirectory.Size                           = New-Object System.Drawing.Size(52, 13)
    $lblDirectory.TabIndex                       = 10
    $lblDirectory.Text                           = "$($PictureActions.$Language.lblDirectory):"
    
    # btnCancel
    #
    $btnCancel.AccessibleDescription             = ''
    $btnCancel.Location                          = New-Object System.Drawing.Point(652, 691)
    $btnCancel.Name                              = 'btnCancel'
    $btnCancel.Size                              = New-Object System.Drawing.Size(120, 50)
    $btnCancel.TabIndex                          = 7
    $btnCancel.Text                              = $($PictureActions.$Language.btnCancel)
    $btnCancel.UseVisualStyleBackColor           = $True
    $ToolTips.SetToolTip($btnCancel, $($PictureActions.$Language.btnCancel_TT))
    $btnCancel.add_Click({
                          $frmTestPicture.Close()
                          $frmTestPicture.Dispose
                          })

    # pgbOverlay
    #
    $pgbOverlay.Location                         = New-Object System.Drawing.Point(12, 691)
    $pgbOverlay.Name                             = 'pgbOverlay'
    $pgbOverlay.Size                             = New-Object System.Drawing.Size(373, 50)
    $pgbOverlay.Style                            = 'Continuous'
    $pgbOverlay.TabIndex                         = 6
    $pgbOverlay.Minimum                          = 1
    $pgbOverlay.Maximum                          = 100    

    # lblProcessing
    #
    $lblProcessing.AutoSize                      = $True
    $lblProcessing.Location                      = New-Object System.Drawing.Point(15, 672)
    $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                        = "$($PictureActions.$Language.lblGoogleAPIKey):"

    # txtGoogleAPIKey
    #
    $txtGoogleAPIKey.Location                    = New-Object System.Drawing.Point(144, 487)
    $txtGoogleAPIKey.Name                        = 'txtGoogleAPIKey'
    $txtGoogleAPIKey.Size                        = New-Object System.Drawing.Size(400, 20)
    $txtGoogleAPIKey.TabIndex                    = 1
    $txtGoogleAPIKey.Text                        = $Global:gblGoogleAPIKey
    $txtGoogleAPIKey.ReadOnly                    = $True
        
    # 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
     }

    If (-not ($Global:gblGoogleAPIKey))
     {
      $btnSetGoogleMapsInfo.Visible              = $False
      $txtMapType.Visible                        = $False
      $lblMapType.Visible                        = $False
      $txtZoomFactor.Visible                     = $False
      $lblZoomFactor.Visible                     = $False
     }

    $txtOutputDir.Text                           = $LogPath
    $txtOutputDir.SelectionStart                 = $txtOutputDir.Text.Length
    $txtOutputDir.ScrollToCaret()
    $txtOutputDir.ReadOnly                       = $True
    
    if ($Global:gblPhotoDetails -eq "full")  {$rdFull.Checked  = $True}
    if ($Global:gblPhotoDetails -eq "basic") {$rdBasic.Checked = $True}
    if ($Global:gblPhotoDetails -eq "none")  {$rdNone.Checked  = $True}
    
    [void]$frmTestPicture.ShowDialog()

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


   }

And the JSON file PictureDetailsWithForms_v12.json with the translations. 

If you want to adjust the languages, please, use my script Translate content of a JSON file with Google, Microsoft and DeepL to do so.

{
    "en":  {
               "Activity":  "Processing all the jpg or jpeg files",
               "AdditionalInformation_TT":  "Additional information",
               "Address":  "Address",
               "btnBrowse":  "Browse",
               "btnBrowse_TT":  "Browse to the location with the files.",
               "btnCancel":  "Cancel",
               "btnCancel_TT":  "Leave the application, no inventory made.",
               "btnOk":  "Apply settings",
               "btnOk_TT":  "Accept the values.",
               "btnReadAPIFromCb":  "Get API Key from clipboard.",
               "btnReadAPIFromCb_TT":  "Make sure that the Google API Key is copied to the clipboard and paste it here.",
               "btnReset":  "Reset",
               "btnReset_TT":  "Reset the settings to its default: zoomfactor = 17 and map type is roadmap.",
               "btnSaveAsHTMLPage":  "Run and save as HTML page",
               "btnSaveAsHTMLPage_TT":  "Run and save as HTML page and quit.",
               "btnSaveASPDF":  "Run and save as PDF file",
               "btnSaveASPDF_TT":  "Run and save as PDF file and quit.",
               "btnSetGoogleMapsInfo":  "Set Google Maps display information",
               "btnSetGoogleMapsInfo_TT":  "Use a GUI to select the zoom factor and map type.",
               "chkIncludeSubfolders":  "Include subfolders",
               "chkIncludeSubfolders_TT":  "Include subfolders as well.",
               "chkOnlyBasicPhotoDetails":  "Only basic photo details",
               "chkOnlyBasicPhotoDetails_TT":  "Only the basic photodetails are shown in the HTML page or PDF file.",
               "Column_GoogleMapsMap":  "Google Maps Map",
               "Column_PhotoData":  "Photo Data",
               "Column_Picture":  "Picture",
               "CreationDate":  "Creation Date",
               "Done":  "Done!",
               "Error":  "Error",
               "Error001":  "There is no working internet connection. The application is quit.",
               "Error002":  "The Powershell Language mode '<zzz>' is not supported.",
               "Error004":  "The minimum screenwidth is <yyy>. The detected screenwidth is <zzz>. Use the <-silent> switch instead.",
               "Error008":  "The minimum screenheight is <yyy>. The detected screenheight is <zzz>. Use the <-silent> switch instead.",
               "Error016":  "The JSON file '<zzz>' with all the translations does not exists.",
               "Error032":  "Silent: the parameter <-Directory> has not been applied or the directory information is not found in the registry.",
               "Error064":  "The zoomfactor should be between 1 and 20",
               "Error128":  "The Google API Key '<zzz>' seems to be invalid.",
               "FallBackSilentMode":  "Falling back to silent mode.",
               "Filename":  "Filename:",
               "FolderNotExists":  "The folder '<zzz>' does not exists.",
               "frmTestPicture":  "Show pictures with details",
               "GPSCoordinates":  "GPS Coordinates",
               "grpPhotoDetails":  "Photo Details",
               "HTMLPageCreated":  "The HTML page 'zzz' has been created.",
               "HTML_Header":  "Complete overview of the pictures used",
               "HTML_SubHeader":  "The pictures",
               "lblDirectory":  "Directory",
               "lblGoogleAPIKey":  "Google API Key",
               "lblMapType":  "Google Maps Maptype",
               "lblOutputDir":  "Output directory:",
               "lblProcessing":  "Processing photo <nnn> (photo <zzz> of <yyy> photos)",
               "lblZoomFactor":  "Google Maps Zoomfactor",
               "NoAPI_1":  "There is no Google Maps API provided.",
               "NoAPI_2":  "Now, there are two options:",
               "NoAPI_3":  "Use the parameter <b>GoogleAPIKey</b> and provide the APIKey.",
               "NoAPI_4":  "Register a new Google API Key via <zzz>. Get an API Key on Google Maps Platform",
               "NoAPI_5":  "There is no working internet connection. So no Google Maps information.",
               "NoCoordinates":  "There are no coordinates found in the picture.",
               "NoDLLFilesLoaded":  "There where errors while loading the DLL files from '<zzz>'. So no PDF file is created.",
               "NoValidAPIKey":  "The Google API Key '<zzz>' seems to be invalid.",
               "PDFFileCreated":  "The PDF file '<zzz>' has been created.",
               "rdBasic":  "Only basic details",
               "rdFull":  "All photo details",
               "rdNone":  "No photo details"
           },
    "de":  {
               "Activity":  "Verarbeitung aller jpg- oder jpeg-Dateien",
               "AdditionalInformation_TT":  "Zusätzliche Informationen",
               "Address":  "Adresse",
               "btnBrowse":  "Durchsuchen Sie",
               "btnBrowse_TT":  "Navigieren Sie zu dem Ort, an dem sich die Dateien befinden.",
               "btnCancel":  "Abbrechen",
               "btnCancel_TT":  "Verlassen Sie die Anwendung, es wird kein Inventar erstellt.",
               "btnOk":  "Einstellungen anwenden",
               "btnOk_TT":  "Übernehmen Sie die Werte.",
               "btnReadAPIFromCb":  "API-Schlüssel aus der Zwischenablage holen.",
               "btnReadAPIFromCb_TT":  "Stellen Sie sicher, dass der Google-API-Schlüssel in die Zwischenablage kopiert wurde, und fügen Sie ihn hier ein.",
               "btnReset":  "Zurücksetzen",
               "btnReset_TT":  "Setzen Sie die Einstellungen auf die Standardwerte zurück: Zoomfaktor = 17 und Kartentyp ist Straßenkarte.",
               "btnSaveAsHTMLPage":  "Ausführen und als HTML-Seite speichern",
               "btnSaveAsHTMLPage_TT":  "Ausführen, als HTML-Seite speichern und beenden.",
               "btnSaveASPDF":  "Ausführen und als PDF-Datei speichern",
               "btnSaveASPDF_TT":  "Ausführen, als PDF-Datei speichern und beenden.",
               "btnSetGoogleMapsInfo":  "Google Maps-Anzeigeinformationen einstellen",
               "btnSetGoogleMapsInfo_TT":  "Verwenden Sie eine grafische Benutzeroberfläche, um den Zoomfaktor und den Kartentyp auszuwählen.",
               "chkIncludeSubfolders":  "Unterordner einbeziehen",
               "chkIncludeSubfolders_TT":  "Schließen Sie auch Unterordner ein.",
               "chkOnlyBasicPhotoDetails":  "Nur grundlegende Fotodetails",
               "chkOnlyBasicPhotoDetails_TT":  "Auf der HTML-Seite oder in der PDF-Datei werden nur die grundlegenden Fotodetails angezeigt.",
               "Column_GoogleMapsMap":  "Google Maps Karte",
               "Column_PhotoData":  "Foto Daten",
               "Column_Picture":  "Bild",
               "CreationDate":  "Datum der Erstellung",
               "Done":  "Erledigt!",
               "Error":  "Fehler",
               "Error001":  "Es besteht keine funktionierende Internetverbindung. Die Anwendung wird beendet.",
               "Error002":  "Der Powershell-Sprachmodus '<zzz>' wird nicht unterstützt.",
               "Error004":  "Die minimale Bildschirmbreite ist <yyy>. Die erkannte Bildschirmbreite ist <zzz>. Verwenden Sie stattdessen den Schalter <-silent>.",
               "Error008":  "Die minimale Bildschirmhöhe ist <yyy>. Die erkannte Bildschirmhöhe ist <zzz>. Verwenden Sie stattdessen den Schalter <-silent>.",
               "Error016":  "Die JSON-Datei '<zzz>' mit allen Übersetzungen ist nicht vorhanden.",
               "Error032":  "Stumm: Der Parameter <-Directory> wurde nicht angewandt oder die Verzeichnisinformationen werden in der Registrierung nicht gefunden.",
               "Error064":  "Der Zoomfaktor sollte zwischen 1 und 20 liegen.",
               "Error128":  "Der Google API-Schlüssel '<zzz>' scheint ungültig zu sein.",
               "FallBackSilentMode":  "Rückfall in den Ruhemodus.",
               "Filename":  "Dateiname:",
               "FolderNotExists":  "Der Ordner '<zzz>' existiert nicht.",
               "frmTestPicture":  "Bilder mit Details anzeigen",
               "GPSCoordinates":  "GPS-Koordinaten",
               "grpPhotoDetails":  "Foto-Details",
               "HTMLPageCreated":  "Die HTML-Seite 'zzz' wurde erstellt.",
               "HTML_Header":  "Vollständige Übersicht über die verwendeten Bilder",
               "HTML_SubHeader":  "Die Bilder",
               "lblDirectory":  "Verzeichnis",
               "lblGoogleAPIKey":  "Google API-Schlüssel",
               "lblMapType":  "Google Maps Kartentyp",
               "lblOutputDir":  "Ausgabeverzeichnis:",
               "lblProcessing":  "Verarbeitung Foto <nnn> (Foto <zzz> von <yyy> Fotos)",
               "lblZoomFactor":  "Google Maps Zoomfaktor",
               "NoAPI_1":  "Es wird keine Google Maps API bereitgestellt.",
               "NoAPI_2":  "Nun gibt es zwei Möglichkeiten:",
               "NoAPI_3":  "Verwenden Sie den Parameter <b>GoogleAPIKey</b> und geben Sie den APIKey an.",
               "NoAPI_4":  "Registrieren Sie einen neuen Google API-Schlüssel über <zzz>. Erhalten Sie einen API-Schlüssel auf der Google Maps-Plattform",
               "NoAPI_5":  "Es gibt keine funktionierende Internetverbindung, also auch keine Google Maps-Informationen.",
               "NoCoordinates":  "Auf dem Bild sind keine Koordinaten zu finden.",
               "NoDLLFilesLoaded":  "Beim Laden der DLL-Dateien von '<zzz>' traten Fehler auf, so dass keine PDF-Datei erstellt wurde.",
               "NoValidAPIKey":  "Der Google API-Schlüssel '<zzz>' scheint ungültig zu sein.",
               "PDFFileCreated":  "Die PDF-Datei '<zzz>' wurde erstellt.",
               "rdBasic":  "Nur grundlegende Angaben",
               "rdFull":  "Alle Fotodetails",
               "rdNone":  "Keine Fotodetails"
           },
    "es":  {
               "Activity":  "Procesar todos los archivos jpg o jpeg",
               "AdditionalInformation_TT":  "Información adicional",
               "Address":  "Dirección",
               "btnBrowse":  "Visite",
               "btnBrowse_TT":  "Navegue hasta la ubicación con los archivos.",
               "btnCancel":  "Cancelar",
               "btnCancel_TT":  "Deje la solicitud, no se ha hecho el inventario.",
               "btnOk":  "Aplicar ajustes",
               "btnOk_TT":  "Acepta los valores.",
               "btnReadAPIFromCb":  "Obtener la clave de la API del portapapeles.",
               "btnReadAPIFromCb_TT":  "Asegúrate de que la clave de la API de Google está copiada en el portapapeles y pégala aquí.",
               "btnReset":  "Reiniciar",
               "btnReset_TT":  "Restablece la configuración por defecto: factor de zoom = 17 y el tipo de mapa es hoja de ruta.",
               "btnSaveAsHTMLPage":  "Ejecutar y guardar como página HTML",
               "btnSaveAsHTMLPage_TT":  "Ejecutar y guardar como página HTML y salir.",
               "btnSaveASPDF":  "Ejecutar y guardar como archivo PDF",
               "btnSaveASPDF_TT":  "Ejecutar y guardar como archivo PDF y salir.",
               "btnSetGoogleMapsInfo":  "Establecer la información de visualización de Google Maps",
               "btnSetGoogleMapsInfo_TT":  "Utiliza una interfaz gráfica para seleccionar el factor de zoom y el tipo de mapa.",
               "chkIncludeSubfolders":  "Incluir subcarpetas",
               "chkIncludeSubfolders_TT":  "Incluya también las subcarpetas.",
               "chkOnlyBasicPhotoDetails":  "Sólo los detalles básicos de las fotos",
               "chkOnlyBasicPhotoDetails_TT":  "En la página HTML o en el archivo PDF sólo se muestran los detalles fotográficos básicos.",
               "Column_GoogleMapsMap":  "Mapa de Google Maps",
               "Column_PhotoData":  "Datos fotográficos",
               "Column_Picture":  "Imagen",
               "CreationDate":  "Fecha de creación",
               "Done":  "¡Hecho!",
               "Error":  "Error",
               "Error001":  "No hay conexión a Internet que funcione. La aplicación está cerrada.",
               "Error002":  "El modo de lenguaje Powershell '<zzz>' no es compatible.",
               "Error004":  "El ancho de pantalla mínimo es de <yyy>. El ancho de pantalla detectado es de <zzz>. Utilice el interruptor <-silent> en su lugar.",
               "Error008":  "La altura mínima de la pantalla es de <yyy>. La altura de la pantalla detectada es de <zzz>. Utilice el interruptor <-silent> en su lugar.",
               "Error016":  "El archivo JSON '<zzz>' con todas las traducciones no existe.",
               "Error032":  "Silencio: el parámetro <-Directory> no se ha aplicado o la información del directorio no se encuentra en el registro.",
               "Error064":  "El factor de zoom debe estar entre 1 y 20",
               "Error128":  "La clave API de Google '<zzz>' parece no ser válida.",
               "FallBackSilentMode":  "Volviendo al modo silencioso.",
               "Filename":  "Nombre del archivo:",
               "FolderNotExists":  "La carpeta '<zzz>' no existe.",
               "frmTestPicture":  "Mostrar imágenes con detalles",
               "GPSCoordinates":  "Coordenadas GPS",
               "grpPhotoDetails":  "Detalles de la foto",
               "HTMLPageCreated":  "La página HTML 'zzz' ha sido creada.",
               "HTML_Header":  "Resumen completo de las imágenes utilizadas",
               "HTML_SubHeader":  "Las imágenes",
               "lblDirectory":  "Directorio",
               "lblGoogleAPIKey":  "Clave API de Google",
               "lblMapType":  "Google Maps Maptype",
               "lblOutputDir":  "Directorio de salida:",
               "lblProcessing":  "Foto de procesamiento <nnn> (foto <zzz> de <yyy> fotos)",
               "lblZoomFactor":  "Factor de zoom de Google Maps",
               "NoAPI_1":  "No se proporciona la API de Google Maps.",
               "NoAPI_2":  "Ahora, hay dos opciones:",
               "NoAPI_3":  "Utiliza el parámetro <b>GoogleAPIKey</b> y proporciona la APIKey.",
               "NoAPI_4":  "Registrar una nueva clave API de Google a través de <zzz>. Obtener una clave API en Google Maps Platform",
               "NoAPI_5":  "No hay conexión a Internet que funcione, por lo que no hay información de Google Maps.",
               "NoCoordinates":  "No se encuentran coordenadas en la imagen.",
               "NoDLLFilesLoaded":  "Hubo errores al cargar los archivos DLL de '<zzz>', por lo que no se creó ningún archivo PDF.",
               "NoValidAPIKey":  "La clave API de Google '<zzz>' parece no ser válida.",
               "PDFFileCreated":  "Se ha creado el archivo PDF '<zzz>'.",
               "rdBasic":  "Sólo detalles básicos",
               "rdFull":  "Todos los detalles de las fotos",
               "rdNone":  "No hay detalles de las fotos"
           },
    "fr":  {
               "Activity":  "Traitement de tous les fichiers jpg ou jpeg",
               "AdditionalInformation_TT":  "Informations complémentaires",
               "Address":  "Adresse",
               "btnBrowse":  "Parcourir",
               "btnBrowse_TT":  "Naviguez jusqu'à l'endroit où se trouvent les fichiers.",
               "btnCancel":  "Annuler",
               "btnCancel_TT":  "Quittez l'application, aucun inventaire n'est fait.",
               "btnOk":  "Appliquer les paramètres",
               "btnOk_TT":  "Acceptez les valeurs.",
               "btnReadAPIFromCb":  "Obtenez la clé API à partir du presse-papiers.",
               "btnReadAPIFromCb_TT":  "Assurez-vous que la clé API Google est copiée dans le presse-papiers et collez-la ici.",
               "btnReset":  "Réinitialiser",
               "btnReset_TT":  "Remettez les paramètres par défaut : facteur de zoom = 17 et le type de carte est une carte routière.",
               "btnSaveAsHTMLPage":  "Exécuter et sauvegarder comme page HTML",
               "btnSaveAsHTMLPage_TT":  "Exécutez et enregistrez comme page HTML et quittez.",
               "btnSaveASPDF":  "Exécuter et sauvegarder en tant que fichier PDF",
               "btnSaveASPDF_TT":  "Exécutez et enregistrez comme fichier PDF et quittez.",
               "btnSetGoogleMapsInfo":  "Définir les informations d'affichage de Google Maps",
               "btnSetGoogleMapsInfo_TT":  "Utilisez une interface graphique pour sélectionner le facteur de zoom et le type de carte.",
               "chkIncludeSubfolders":  "Inclure les sous-dossiers",
               "chkIncludeSubfolders_TT":  "Incluez également les sous-dossiers.",
               "chkOnlyBasicPhotoDetails":  "Seulement les détails de base de la photo",
               "chkOnlyBasicPhotoDetails_TT":  "Seuls les détails des photos de base sont affichés dans la page HTML ou le fichier PDF.",
               "Column_GoogleMapsMap":  "Carte Google Maps",
               "Column_PhotoData":  "Photo Data",
               "Column_Picture":  "Photo",
               "CreationDate":  "Date de création",
               "Done":  "C'est fait !",
               "Error":  "Erreur",
               "Error001":  "Il n'y a pas de connexion internet fonctionnelle. L'application est abandonnée.",
               "Error002":  "Le mode de langage Powershell '<zzz>' n'est pas pris en charge.",
               "Error004":  "La largeur d'écran minimale est de <yyy>. La largeur d'écran détectée est de <zzz>. Utilisez plutôt le commutateur <-silent>.",
               "Error008":  "La hauteur d'écran minimale est de <yyy>. La hauteur d'écran détectée est de <zzz>. Utilisez plutôt le commutateur <-silent>.",
               "Error016":  "Le fichier JSON '<zzz>' contenant toutes les traductions n'existe pas.",
               "Error032":  "Silencieux : le paramètre <-Directory> n'a pas été appliqué ou les informations du répertoire ne sont pas trouvées dans le registre.",
               "Error064":  "Le facteur de zoom doit être compris entre 1 et 20.",
               "Error128":  "La clé API Google '<zzz>' semble être invalide.",
               "FallBackSilentMode":  "Revenir en mode silencieux.",
               "Filename":  "Filename :",
               "FolderNotExists":  "Le dossier '<zzz>' n'existe pas.",
               "frmTestPicture":  "Afficher les photos avec les détails",
               "GPSCoordinates":  "Coordonnées GPS",
               "grpPhotoDetails":  "Détails de la photo",
               "HTMLPageCreated":  "La page HTML 'zzz' a été créée.",
               "HTML_Header":  "Aperçu complet des images utilisées",
               "HTML_SubHeader":  "Les photos",
               "lblDirectory":  "Annuaire",
               "lblGoogleAPIKey":  "Clé API Google",
               "lblMapType":  "Google Maps Maptype",
               "lblOutputDir":  "Répertoire de sortie :",
               "lblProcessing":  "Traitement de la photo <nnn> (photo <zzz> de <yyy> photos)",
               "lblZoomFactor":  "Facteur de zoom de Google Maps",
               "NoAPI_1":  "Aucune API Google Maps n'est fournie.",
               "NoAPI_2":  "Maintenant, il y a deux options :",
               "NoAPI_3":  "Utilisez le paramètre <b>GoogleAPIKey</b> et fournissez le APIKey.",
               "NoAPI_4":  "Enregistrer une nouvelle clé API Google via <zzz>. Obtenir une clé API sur la plateforme Google Maps",
               "NoAPI_5":  "Il n'y a pas de connexion internet qui fonctionne, donc pas d'informations sur Google Maps.",
               "NoCoordinates":  "Il n'y a pas de coordonnées dans l'image.",
               "NoDLLFilesLoaded":  "Il y a eu des erreurs lors du chargement des fichiers DLL de '<zzz>', donc aucun fichier PDF n'a été créé.",
               "NoValidAPIKey":  "La clé API Google '<zzz>' semble être invalide.",
               "PDFFileCreated":  "Le fichier PDF '<zzz>' a été créé.",
               "rdBasic":  "Seulement les détails de base",
               "rdFull":  "Tous les détails des photos",
               "rdNone":  "Pas de détails sur la photo"
           },
    "id":  {
               "Activity":  "Memproses semua file jpg atau jpeg",
               "AdditionalInformation_TT":  "Informasi tambahan",
               "Address":  "Alamat",
               "btnBrowse":  "Jelajahi",
               "btnBrowse_TT":  "Jelajahi lokasi dengan file-file tersebut.",
               "btnCancel":  "Batal",
               "btnCancel_TT":  "Tinggalkan aplikasi, tidak ada inventaris yang dibuat.",
               "btnOk":  "Terapkan pengaturan",
               "btnOk_TT":  "Menerima nilai-nilai.",
               "btnReadAPIFromCb":  "Dapatkan Kunci API dari clipboard.",
               "btnReadAPIFromCb_TT":  "Pastikan bahwa Google API Key disalin ke clipboard dan tempelkan di sini.",
               "btnReset":  "Atur ulang",
               "btnReset_TT":  "Atur ulang pengaturan ke default: zoomfactor = 17 dan tipe peta adalah roadmap.",
               "btnSaveAsHTMLPage":  "Jalankan dan simpan sebagai halaman HTML",
               "btnSaveAsHTMLPage_TT":  "Jalankan dan simpan sebagai halaman HTML dan keluar.",
               "btnSaveASPDF":  "Jalankan dan simpan sebagai file PDF",
               "btnSaveASPDF_TT":  "Jalankan dan simpan sebagai file PDF dan keluar.",
               "btnSetGoogleMapsInfo":  "Mengatur informasi tampilan Google Maps",
               "btnSetGoogleMapsInfo_TT":  "Gunakan GUI untuk memilih faktor zoom dan jenis peta.",
               "chkIncludeSubfolders":  "Sertakan subfolder",
               "chkIncludeSubfolders_TT":  "Sertakan juga subfolder.",
               "chkOnlyBasicPhotoDetails":  "Hanya detail foto dasar",
               "chkOnlyBasicPhotoDetails_TT":  "Hanya detail foto dasar yang ditampilkan dalam halaman HTML atau file PDF.",
               "Column_GoogleMapsMap":  "Peta Google Maps",
               "Column_PhotoData":  "Data Foto",
               "Column_Picture":  "Gambar",
               "CreationDate":  "Tanggal Pembuatan",
               "Done":  "Selesai!",
               "Error":  "Kesalahan",
               "Error001":  "Tidak ada koneksi internet yang berfungsi. Aplikasi berhenti.",
               "Error002":  "Mode Bahasa Powershell '<zzz>' tidak didukung.",
               "Error004":  "Lebar layar minimum adalah <yyy>. Lebar layar yang terdeteksi adalah <zzz>. Gunakan sakelar <-silent> sebagai gantinya.",
               "Error008":  "Tinggi layar minimum adalah <yyy>. Tinggi layar yang terdeteksi adalah <zzz>. Gunakan sakelar <-silent> sebagai gantinya.",
               "Error016":  "File JSON '<zzz>' dengan semua terjemahan tidak ada.",
               "Error032":  "Silent: parameter <-Directory> belum diterapkan atau informasi direktori tidak ditemukan dalam registri.",
               "Error064":  "Faktor zoom harus antara 1 dan 20",
               "Error128":  "Kunci API Google '<zzz>' tampaknya tidak valid.",
               "FallBackSilentMode":  "Kembali ke mode hening.",
               "Filename":  "Nama file:",
               "FolderNotExists":  "Folder '<zzz>' tidak ada.",
               "frmTestPicture":  "Tampilkan gambar dengan detail",
               "GPSCoordinates":  "Koordinat GPS",
               "grpPhotoDetails":  "Detail Foto",
               "HTMLPageCreated":  "Halaman HTML 'zzz' telah dibuat.",
               "HTML_Header":  "Ikhtisar lengkap gambar yang digunakan",
               "HTML_SubHeader":  "Gambar-gambar",
               "lblDirectory":  "Direktori",
               "lblGoogleAPIKey":  "Kunci API Google",
               "lblMapType":  "Google Maps Jenis Peta",
               "lblOutputDir":  "Direktori output:",
               "lblProcessing":  "Memproses foto <nnn> (foto <zzz> dari <yyy> foto)",
               "lblZoomFactor":  "Zoomfactor Google Maps",
               "NoAPI_1":  "Tidak ada Google Maps API yang disediakan.",
               "NoAPI_2":  "Sekarang, ada dua opsi:",
               "NoAPI_3":  "Gunakan parameter <b>GoogleAPIKey</b> dan berikan APIKey.",
               "NoAPI_4":  "Daftarkan Kunci API Google baru melalui <zzz>. Dapatkan Kunci API di Platform Google Maps",
               "NoAPI_5":  "Tidak ada koneksi internet yang berfungsi. Jadi tidak ada informasi Google Maps.",
               "NoCoordinates":  "Tidak ada koordinat yang ditemukan dalam gambar.",
               "NoDLLFilesLoaded":  "Ada kesalahan saat memuat file DLL dari '<zzz>'. Jadi tidak ada file PDF yang dibuat.",
               "NoValidAPIKey":  "Kunci API Google '<zzz>' tampaknya tidak valid.",
               "PDFFileCreated":  "File PDF '<zzz>' telah dibuat.",
               "rdBasic":  "Hanya detail dasar",
               "rdFull":  "Semua detail foto",
               "rdNone":  "Tidak ada detail foto"
           },
    "it":  {
               "Activity":  "Elaborazione di tutti i file jpg o jpeg",
               "AdditionalInformation_TT":  "Informazioni aggiuntive",
               "Address":  "Indirizzo",
               "btnBrowse":  "Sfogliare",
               "btnBrowse_TT":  "Cercare il percorso con i file.",
               "btnCancel":  "Annullamento",
               "btnCancel_TT":  "Lasciare l'applicazione, senza inventario.",
               "btnOk":  "Applicare le impostazioni",
               "btnOk_TT":  "Accettare i valori.",
               "btnReadAPIFromCb":  "Ottenere la chiave API dagli appunti.",
               "btnReadAPIFromCb_TT":  "Assicurarsi che la chiave API di Google sia copiata negli appunti e incollarla qui.",
               "btnReset":  "Reset",
               "btnReset_TT":  "Ripristinare le impostazioni predefinite: zoomfactor = 17 e tipo di mappa è roadmap.",
               "btnSaveAsHTMLPage":  "Eseguire e salvare come pagina HTML",
               "btnSaveAsHTMLPage_TT":  "Eseguire e salvare come pagina HTML e uscire.",
               "btnSaveASPDF":  "Eseguire e salvare come file PDF",
               "btnSaveASPDF_TT":  "Eseguire e salvare come file PDF e uscire.",
               "btnSetGoogleMapsInfo":  "Impostare le informazioni di visualizzazione di Google Maps",
               "btnSetGoogleMapsInfo_TT":  "Utilizzare un'interfaccia grafica per selezionare il fattore di zoom e il tipo di mappa.",
               "chkIncludeSubfolders":  "Includere le sottocartelle",
               "chkIncludeSubfolders_TT":  "Includere anche le sottocartelle.",
               "chkOnlyBasicPhotoDetails":  "Solo i dettagli delle foto di base",
               "chkOnlyBasicPhotoDetails_TT":  "Nella pagina HTML o nel file PDF vengono mostrati solo i dettagli fotografici di base.",
               "Column_GoogleMapsMap":  "Google Maps Mappa",
               "Column_PhotoData":  "Dati fotografici",
               "Column_Picture":  "Immagine",
               "CreationDate":  "Data di creazione",
               "Done":  "Fatto!",
               "Error":  "Errore",
               "Error001":  "La connessione a Internet non funziona e l'applicazione è stata abbandonata.",
               "Error002":  "La modalità del linguaggio Powershell '<zzz>' non è supportata.",
               "Error004":  "La larghezza minima dello schermo è <yyy>. La larghezza dello schermo rilevata è <zzz>. Utilizzare invece l'interruttore <-silent>.",
               "Error008":  "L'altezza minima dello schermo è <yyy>. L'altezza dello schermo rilevata è <zzz>. Utilizzare invece l'interruttore <-silent>.",
               "Error016":  "Il file JSON '<zzz>' con tutte le traduzioni non esiste.",
               "Error032":  "Silenzioso: il parametro <-Directory> non è stato applicato o le informazioni sulla directory non sono presenti nel registro.",
               "Error064":  "Il fattore di zoom deve essere compreso tra 1 e 20.",
               "Error128":  "La chiave API di Google '<zzz>' sembra non essere valida.",
               "FallBackSilentMode":  "Ricaduta in modalità silenziosa.",
               "Filename":  "Nome del file:",
               "FolderNotExists":  "La cartella '<zzz>' non esiste.",
               "frmTestPicture":  "Mostra le immagini con i dettagli",
               "GPSCoordinates":  "Coordinate GPS",
               "grpPhotoDetails":  "Dettagli foto",
               "HTMLPageCreated":  "È stata creata la pagina HTML 'zzz'.",
               "HTML_Header":  "Panoramica completa delle immagini utilizzate",
               "HTML_SubHeader":  "Le immagini",
               "lblDirectory":  "Elenco",
               "lblGoogleAPIKey":  "Chiave API di Google",
               "lblMapType":  "Google Maps Tipo di mappa",
               "lblOutputDir":  "Directory di uscita:",
               "lblProcessing":  "Elaborazione della foto <nnn> (foto <zzz> di <yyy> foto)",
               "lblZoomFactor":  "Fattore di zoom di Google Maps",
               "NoAPI_1":  "Non viene fornita l'API di Google Maps.",
               "NoAPI_2":  "Ora, ci sono due opzioni:",
               "NoAPI_3":  "Utilizzare il parametro <b>GoogleAPIKey</b> e fornire l'APIKey.",
               "NoAPI_4":  "Registrare una nuova chiave API di Google tramite <zzz>. Ottenere una chiave API sulla piattaforma Google Maps",
               "NoAPI_5":  "Non c'è una connessione internet funzionante, quindi niente informazioni su Google Maps.",
               "NoCoordinates":  "L'immagine non contiene coordinate.",
               "NoDLLFilesLoaded":  "Si sono verificati errori durante il caricamento dei file DLL da '<zzz>'. Quindi non viene creato alcun file PDF.",
               "NoValidAPIKey":  "La chiave API di Google '<zzz>' sembra non essere valida.",
               "PDFFileCreated":  "Il file PDF '<zzz>' è stato creato.",
               "rdBasic":  "Solo dettagli di base",
               "rdFull":  "Tutti i dettagli delle foto",
               "rdNone":  "Nessun dettaglio fotografico"
           },
    "nl":  {
               "Activity":  "Alle jpg- of jpeg-bestanden verwerken",
               "AdditionalInformation_TT":  "Aanvullende informatie",
               "Address":  "Adres",
               "btnBrowse":  "Bladeren op",
               "btnBrowse_TT":  "Blader naar de locatie met de bestanden.",
               "btnCancel":  "Annuleren",
               "btnCancel_TT":  "Verlaat de toepassing, geen inventarisatie gemaakt.",
               "btnOk":  "Instellingen toepassen",
               "btnOk_TT":  "Accepteer de waarden.",
               "btnReadAPIFromCb":  "Haal de API sleutel van het klembord.",
               "btnReadAPIFromCb_TT":  "Zorg ervoor dat de Google API Key naar het klembord wordt gekopieerd en plak deze hier.",
               "btnReset":  "Reset",
               "btnReset_TT":  "Zet de instellingen terug naar de standaard: zoomfactor = 17 en kaarttype is wegenkaart.",
               "btnSaveAsHTMLPage":  "Uitvoeren en opslaan als HTML-pagina",
               "btnSaveAsHTMLPage_TT":  "Uitvoeren en opslaan als HTML-pagina en afsluiten.",
               "btnSaveASPDF":  "Uitvoeren en opslaan als PDF-bestand",
               "btnSaveASPDF_TT":  "Uitvoeren en opslaan als PDF-bestand en afsluiten.",
               "btnSetGoogleMapsInfo":  "Google Maps weergave-informatie instellen",
               "btnSetGoogleMapsInfo_TT":  "Gebruik een GUI om de zoomfactor en het kaarttype te selecteren.",
               "chkIncludeSubfolders":  "Submappen opnemen",
               "chkIncludeSubfolders_TT":  "Inclusief submappen.",
               "chkOnlyBasicPhotoDetails":  "Alleen basisfoto's",
               "chkOnlyBasicPhotoDetails_TT":  "Alleen de basisfotodetails worden getoond in de HTML-pagina of het PDF-bestand.",
               "Column_GoogleMapsMap":  "Google Maps Kaart",
               "Column_PhotoData":  "Fotogegevens",
               "Column_Picture":  "Foto",
               "CreationDate":  "Datum van oprichting",
               "Done":  "Klaar!",
               "Error":  "Fout",
               "Error001":  "Er is geen werkende internetverbinding. De applicatie is gestopt.",
               "Error002":  "De Powershell-taalmodus '<zzz>' wordt niet ondersteund.",
               "Error004":  "De minimale schermbreedte is <yyy>. De gedetecteerde schermbreedte is <zzz>. Gebruik in plaats daarvan de schakelaar <-silent>.",
               "Error008":  "De minimale schermhoogte is <yyy>. De gedetecteerde schermhoogte is <zzz>. Gebruik in plaats daarvan de schakelaar <-silent>.",
               "Error016":  "Het JSON-bestand '<zzz>' met alle vertalingen bestaat niet.",
               "Error032":  "Stil: de parameter <-Directory> is niet toegepast of de directory informatie is niet gevonden in het register.",
               "Error064":  "De zoomfactor moet tussen 1 en 20 liggen.",
               "Error128":  "De Google API-sleutel '<zzz>' lijkt ongeldig te zijn.",
               "FallBackSilentMode":  "Terug naar de stille modus.",
               "Filename":  "Bestandsnaam:",
               "FolderNotExists":  "De map '<zzz>' bestaat niet.",
               "frmTestPicture":  "Toon foto's met details",
               "GPSCoordinates":  "GPS Coördinaten",
               "grpPhotoDetails":  "Foto Details",
               "HTMLPageCreated":  "De HTML-pagina 'zzz' is aangemaakt.",
               "HTML_Header":  "Volledig overzicht van de gebruikte foto's",
               "HTML_SubHeader":  "De foto's",
               "lblDirectory":  "Directory",
               "lblGoogleAPIKey":  "Google API sleutel",
               "lblMapType":  "Google Maps Maptype",
               "lblOutputDir":  "Uitvoermap:",
               "lblProcessing":  "Verwerking foto <nnn> (foto <zzz> van <yyy> foto's)",
               "lblZoomFactor":  "Google Maps zoomfactor",
               "NoAPI_1":  "Er is geen Google Maps API voorzien.",
               "NoAPI_2":  "Er zijn twee opties:",
               "NoAPI_3":  "Gebruik de parameter <b>GoogleAPIKey</b> en geef de APIKey op.",
               "NoAPI_4":  "Registreer een nieuwe Google API Key via <zzz>. Verkrijg een API Key op het Google Maps Platform",
               "NoAPI_5":  "Er is geen werkende internetverbinding. Dus geen Google Maps informatie.",
               "NoCoordinates":  "Er zijn geen coördinaten op de foto.",
               "NoDLLFilesLoaded":  "Er waren fouten bij het laden van de DLL-bestanden van '<zzz>'. Er is dus geen PDF-bestand gemaakt.",
               "NoValidAPIKey":  "De Google API sleutel '<zzz>' lijkt ongeldig te zijn.",
               "PDFFileCreated":  "Het PDF-bestand '<zzz>' is aangemaakt.",
               "rdBasic":  "Alleen basisgegevens",
               "rdFull":  "Alle fotodetails",
               "rdNone":  "Geen foto details"
           },
    "pt":  {
               "Activity":  "Processamento de todos os ficheiros jpg ou jpeg",
               "AdditionalInformation_TT":  "Informação adicional",
               "Address":  "Endereço",
               "btnBrowse":  "Navegar",
               "btnBrowse_TT":  "Navegue até ao local com os ficheiros.",
               "btnCancel":  "Cancelar",
               "btnCancel_TT":  "Deixar o pedido, sem inventário feito.",
               "btnOk":  "Aplicar definições",
               "btnOk_TT":  "Aceitar os valores.",
               "btnReadAPIFromCb":  "Obtenha a chave API a partir da prancheta.",
               "btnReadAPIFromCb_TT":  "Certifique-se de que a chave API do Google é copiada para a área de transferência e cole-a aqui.",
               "btnReset":  "Reinicialização",
               "btnReset_TT":  "Repor as definições ao seu padrão: zoomfactor = 17 e tipo de mapa é roteiro.",
               "btnSaveAsHTMLPage":  "Executar e guardar como página HTML",
               "btnSaveAsHTMLPage_TT":  "Executar e guardar como página HTML e desistir.",
               "btnSaveASPDF":  "Executar e guardar como ficheiro PDF",
               "btnSaveASPDF_TT":  "Executar e guardar como ficheiro PDF e desistir.",
               "btnSetGoogleMapsInfo":  "Definir a informação de visualização do Google Maps",
               "btnSetGoogleMapsInfo_TT":  "Utilizar uma GUI para seleccionar o factor de zoom e o tipo de mapa.",
               "chkIncludeSubfolders":  "Incluir subpastas",
               "chkIncludeSubfolders_TT":  "Incluir também subpastas.",
               "chkOnlyBasicPhotoDetails":  "Apenas detalhes fotográficos básicos",
               "chkOnlyBasicPhotoDetails_TT":  "Apenas os detalhes básicos das fotografias são mostrados na página HTML ou no ficheiro PDF.",
               "Column_GoogleMapsMap":  "Mapa Google Maps",
               "Column_PhotoData":  "Dados fotográficos",
               "Column_Picture":  "Fotografia",
               "CreationDate":  "Data de criação",
               "Done":  "Feito!",
               "Error":  "Erro",
               "Error001":  "Não existe uma ligação à Internet que funcione. A aplicação é abandonada.",
               "Error002":  "O modo de linguagem Powershell '<zzz>' não é suportado.",
               "Error004":  "A largura mínima de ecrã é de <yyy>. A largura de ecrã detectada é de <zzz>. Em vez disso, utilize o interruptor <-silent>.",
               "Error008":  "A altura mínima de ecrã é de <yyy>. A altura de ecrã detectada é de <zzz>. Em vez disso, utilize o interruptor <-silent>.",
               "Error016":  "O ficheiro do JSON '<zzz>' com todas as traduções não existe.",
               "Error032":  "Silencioso: o parâmetro <-Directory> não foi aplicado ou a informação do directório não é encontrada no registo.",
               "Error064":  "O factor de zoom deve estar entre 1 e 20",
               "Error128":  "A chave API do Google '<zzz>' parece ser inválida.",
               "FallBackSilentMode":  "Voltando ao modo silencioso.",
               "Filename":  "Nome do ficheiro:",
               "FolderNotExists":  "A pasta '<zzz>' não existe.",
               "frmTestPicture":  "Mostrar fotografias com detalhes",
               "GPSCoordinates":  "Coordenadas GPS",
               "grpPhotoDetails":  "Detalhes da foto",
               "HTMLPageCreated":  "A página HTML 'zzz' foi criada.",
               "HTML_Header":  "Visão geral completa das imagens utilizadas",
               "HTML_SubHeader":  "As imagens",
               "lblDirectory":  "Directório",
               "lblGoogleAPIKey":  "Chave API do Google",
               "lblMapType":  "Tipo de mapa Google Maps",
               "lblOutputDir":  "Directório de saída:",
               "lblProcessing":  "Foto de processamento <nnn> (foto <zzz> de <yyy> fotos)",
               "lblZoomFactor":  "Google Maps Zoomfactor",
               "NoAPI_1":  "Não é fornecida nenhuma API do Google Maps.",
               "NoAPI_2":  "Agora, há duas opções:",
               "NoAPI_3":  "Utilizar o parâmetro <b>GoogleAPIKey</b> e fornecer a APIKey.",
               "NoAPI_4":  "Registe uma nova chave API do Google através do <zzz>. Obtenha uma chave API na plataforma Google Maps",
               "NoAPI_5":  "Não existe uma ligação à Internet que funcione, pelo que não existe informação no Google Maps.",
               "NoCoordinates":  "Não existem coordenadas encontradas na fotografia.",
               "NoDLLFilesLoaded":  "Aí onde se cometem erros ao carregar os ficheiros DLL do '<zzz>'. Assim, não é criado nenhum ficheiro PDF.",
               "NoValidAPIKey":  "A chave API do Google '<zzz>' parece ser inválida.",
               "PDFFileCreated":  "O ficheiro PDF '<zzz>' foi criado.",
               "rdBasic":  "Apenas detalhes básicos",
               "rdFull":  "Todos os detalhes da foto",
               "rdNone":  "Sem detalhes fotográficos"
           },
    "zh":  {
               "Activity":  "处理所有的jpg或jpeg文件",
               "AdditionalInformation_TT":  "其他信息",
               "Address":  "地址",
               "btnBrowse":  "浏览",
               "btnBrowse_TT":  "浏览到有文件的位置。",
               "btnCancel":  "取消",
               "btnCancel_TT":  "离开申请,没有进行清点。",
               "btnOk":  "应用设置",
               "btnOk_TT":  "接受这些值。",
               "btnReadAPIFromCb":  "从剪贴板上获取API密钥。",
               "btnReadAPIFromCb_TT":  "确保谷歌API密钥被复制到剪贴板上,并将其粘贴在这里。",
               "btnReset":  "复位",
               "btnReset_TT":  "将设置重置为默认值:缩放因子=17,地图类型为路线图。",
               "btnSaveAsHTMLPage":  "运行并保存为HTML页面",
               "btnSaveAsHTMLPage_TT":  "运行并保存为HTML页面并退出。",
               "btnSaveASPDF":  "运行并保存为PDF文件",
               "btnSaveASPDF_TT":  "运行并保存为PDF文件并退出。",
               "btnSetGoogleMapsInfo":  "设置谷歌地图显示信息",
               "btnSetGoogleMapsInfo_TT":  "使用GUI来选择缩放系数和地图类型。",
               "chkIncludeSubfolders":  "包括子文件夹",
               "chkIncludeSubfolders_TT":  "也包括子文件夹。",
               "chkOnlyBasicPhotoDetails":  "只有基本的照片细节",
               "chkOnlyBasicPhotoDetails_TT":  "在HTML页面或PDF文件中只显示基本的照片细节。",
               "Column_GoogleMapsMap":  "谷歌地图",
               "Column_PhotoData":  "照片数据",
               "Column_Picture":  "图片",
               "CreationDate":  "创建日期",
               "Done":  "完成了!",
               "Error":  "误差",
               "Error001":  "没有工作的互联网连接。 应用程序被退出。",
               "Error002":  "不支持Powershell语言模式'<zzz>'。",
               "Error004":  "最小的屏幕宽度是<yyy>。 检测到的屏幕宽度是<zzz>。 使用<-silent>开关代替。",
               "Error008":  "最小的屏幕高度是<yyy>。 检测到的屏幕高度是<zzz>。 使用<-silent>开关代替。",
               "Error016":  "包含所有翻译的JSON文件'<zzz>'不存在。",
               "Error032":  "沉默:参数<-Directory>没有被应用,或者在注册表中没有找到目录信息。",
               "Error064":  "缩放系数应在1至20之间。",
               "Error128":  "谷歌API密钥'<zzz>'似乎是无效的。",
               "FallBackSilentMode":  "回落到无声模式。",
               "Filename":  "文件名。",
               "FolderNotExists":  "文件夹'<zzz>'并不存在。",
               "frmTestPicture":  "显示有细节的图片",
               "GPSCoordinates":  "GPS坐标",
               "grpPhotoDetails":  "照片细节",
               "HTMLPageCreated":  "已经创建了HTML页面'zz'。",
               "HTML_Header":  "所用图片的完整概述",
               "HTML_SubHeader":  "图片",
               "lblDirectory":  "指南",
               "lblGoogleAPIKey":  "谷歌API密钥",
               "lblMapType":  "谷歌地图 地图类型",
               "lblOutputDir":  "输出目录。",
               "lblProcessing":  "处理照片<nnn>(照片<zzz>,共<yyy>张)。",
               "lblZoomFactor":  "谷歌地图缩放因子",
               "NoAPI_1":  "没有提供谷歌地图API。",
               "NoAPI_2":  "现在,有两个选择。",
               "NoAPI_3":  "使用参数<b>GoogleAPIKey</b>并提供APIKey。",
               "NoAPI_4":  "通过<zzz>注册一个新的谷歌API密钥。 在谷歌地图平台上获得一个API密钥",
               "NoAPI_5":  "没有工作的互联网连接。 所以没有谷歌地图信息。",
               "NoCoordinates":  "图片中没有发现坐标。",
               "NoDLLFilesLoaded":  "在从'<zzz>'加载DLL文件时出现了错误。 因此没有创建PDF文件。",
               "NoValidAPIKey":  "谷歌API密钥'<zzz>'似乎是无效的。",
               "PDFFileCreated":  "PDF文件'<zzz>'已被创建。",
               "rdBasic":  "只有基本的细节",
               "rdFull":  "所有照片细节",
               "rdNone":  "没有照片细节"
           }
}

 








Please 'unblock' the Powershell script and/or the batch file after extracting the ZIP file. 

Unblock
Unblock