Bookmark and Share

 

This week saw the kickoff and first week of the 2010 Scripting Games. The Scripting Wife is in the midst of things, so we decided to give her a break and let her catch her breath. You can catch up with her progress by reviewing the articles in the archive.

Microsoft Scripting Guy Ed Wilson here. Yesterday, I wrote a quick script that will convert a .png file to a .jpg file. As I was looking at it again, I decided that it would be pretty cool to expand upon it a little bit because I can see myself using it on a regular basis. In fact, it will convert .bmp or other image file formats to a .jpg format as well. If you use a different format enumeration value, it will save to image types besides .jpg.

I created a function, added help to the function, and configured it in such a way that it accepts piped input. The resulting script is pretty useful, and the advanced function itself is ready to be copied into a profile or a module.

ConvertTo-JpgFunction.ps1

#Requires -version 2.0
Param($path = "C:\fso")

Function ConvertTo-Jpg
{
 <#
  .Synopsis
    Creates copies of images into jpg files. The new
    image file has the same name as original, but new extension. New
    images are automatically stored in same directory as originals.
   .Example
    ConvertTo-Jpg -sourceFile "C:\fso\image.png"
    Creates image.jpg from image.png
   .Example
    Get-ChildItem -Path c:\fso -Include *.png -Recurse | ConvertTo-Jpg
    Creates .jpg images from all png images in c:\fso directory
   .Parameter SourceFile
    The image file to be converted to JPG
   .Inputs
    [string]
   .Outputs
    [Drawing.Image]
   .Notes
    NAME:  ConvertTo-Jpg
    AUTHOR: Ed Wilson
    AUTHOR BOOK: Windows PowerShell 2.0 Best Practices, Microsoft Press 2010
    LASTEDIT: 1/31/2010
    KEYWORDS: Weekend Scripter, Multimedia, Graphics
   .Link
     Http://www.ScriptingGuys.com
     Http://www.bit.ly/HSGBlog
 #>
 #Requires -Version 2.0
 [CmdletBinding()]
 param(
      [Parameter(Mandatory = $True,valueFromPipeline=$true)]
      $SourceFile
) #end param
 BEGIN {
   Try
     {
      $drawing = "system.drawing.image" -as [type]    
      $imageFormat = "System.Drawing.Imaging.ImageFormat" -as [type]                                                                       
      [reflection.assembly]::GetAssembly($drawing) | Out-Null
     }
   Catch [system.Exception]
     {
      "Assembly not loaded. Loading now."
      Add-Type -AssemblyName system.drawing
      $imageFormat = "System.Drawing.Imaging.ImageFormat" -as [type]
     }
    } #end begin
 PROCESS {
    $saveFile = Join-Path -Path $_.directory -ChildPath ($_.BaseName + ".jpg")
    If(!(Test-Path -Path $saveFile))
      {
       $image = [drawing.image]::FromFile($sourceFile)
       $image.Save($saveFile, $imageFormat::jpeg)
      }
    Else { "$saveFile already exists. Skipping ..." }
   } #end process
} #end function ConvertTo-Jpg

# *** Entry Point to Script ***

If(!$path) { "You must supply path to images for processing";exit }
Get-ChildItem -Path $path -Include *.png -Recurse | ConvertTo-Jpg

Keep in mind that in addition to the ConvertTo-Jpg function, there is also code in the script itself. The script accepts command-line input that allows you to call the script and pass a folder for the parameter. The script then checks to see if the folder exists, and if it does, it calls Get-ChildItem to retrieve all of the .png files in the folder, and then passes the collection to the ConvertTo-Jpg function. The $path parameter is assigned a default value of c:\fso that probably will not exist on your computer. This is shown here:

Param($path = "C:\fso")

The entry point to the script is shown here:

If(!$path) { "You must supply path to images for processing";exit }
Get-ChildItem -Path $path -Include *.png -Recurse | ConvertTo-Jpg

The help section of the ConvertTo-Jpg function is seen here. Adding help tags to a function was discussed in a recent Hey, Scripting Guy! post:

<#
  .Synopsis
    Creates copies of images into jpg files. The new
    image file has same name as original, but new extension. New
    images are automatically stored in same directory as originals.
   .Example
    ConvertTo-Jpg -sourceFile "C:\fso\image.png"
    Creates image.jpg from image.png
   .Example
    Get-ChildItem -Path c:\fso -Include *.png -Recurse | ConvertTo-Jpg
    Creates .jpg images from all png images in c:\fso directory
   .Parameter SourceFile
    The image file to be converted to JPG
   .Inputs
    [string]
   .Outputs
    [Drawing.Image]
   .Notes
    NAME:  ConvertTo-Jpg
    AUTHOR: Ed Wilson
    AUTHOR BOOK: Windows PowerShell 2.0 Best Practices, Microsoft Press 2010
    LASTEDIT: 1/31/2010
    KEYWORDS: WeekEnd Scripter, Multimedia, Graphics
   .Link
     Http://www.scriptingguys.com
     Http://www.bit.ly/hsgblog
 #>

The next lines of code ensure the function is running on Windows PowerShell 2.0, turn on cmdlet binding, and define the $sourceFile parameter as mandatory:

#Requires -Version 2.0
 [CmdletBinding()]
 param(
      [Parameter(Mandatory = $True,valueFromPipeline=$true)]
      $SourceFile
) #end param

The BEGIN section of the function uses Try/Catch to ensure that the System.Drawing .NET Framework assembly is loaded and the ImageFormat type is created. These concepts were discussed yesterday. Try/Catch was discussed in a recent Hey, Scripting Guy! post. To determine if the System.Drawing .NET Framework assembly has been loaded, the GetAssembly static method from the Reflection.Assembly class is used. This method requires a type, and that is why the drawing type is created. This section of the script is shown here:

BEGIN {
   Try
     {
      $drawing = "system.drawing.image" -as [type]    
      $imageFormat = "System.Drawing.Imaging.ImageFormat" -as [type]                                                                       
      [reflection.assembly]::GetAssembly($drawing) | Out-Null
     }
   Catch [system.Exception]
     {
      "Assembly not loaded. Loading now."
      Add-Type -AssemblyName system.drawing
      $imageFormat = "System.Drawing.Imaging.ImageFormat" -as [type]
     }
    } #end begin

In the PROCESS section of the function, the script uses the Join-Path cmdlet to create the path to the new image file that will be created. It uses the directory property from the current item on the pipeline for the parent path, and the basename property to get the file name portion of the path. The .jpg file extension is concatenated onto the basename property to complete the childpath. The Test-Path cmdlet is then used to see if the file already exists. If it does not, the image is converted to the new file format. This part of the script works the same as the script from yesterday.

PROCESS {
    $saveFile = Join-Path -Path $_.directory -ChildPath ($_.BaseName + ".jpg")
    If(!(Test-Path -Path $saveFile))
      {
       $image = [drawing.image]::FromFile($sourceFile)
       $image.Save($saveFile, $imageFormat::jpeg)
      }
    Else { "$saveFile already exists. Skipping ..." }
   } #end process
} #end function ConvertTo-Jpg


The results from running the script are seen in the following image.

Image of results of running script

 

The 2010 Scripting Games continue tomorrow. We will be revealing Events 6–10 tomorrow through Friday. If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys