I can't seem to get PowerShell to utilize the SaveAdd method for a multipage TIFF. Before I start getting critical comments about the pointlessness of the snippet, this is part of a larger script and is only a test bed I created to work my way through the TIFF image manipulation. So all I am trying to do here is convert a multipage TIFF into a new multipage TIFF with a bit depth of 1 and a resolution of 200 ppi (some baggage from the original script is still present).
The only issue I am having is with the SaveAdd()
method. I am sure there is something simple I have overlooked. With very few TIFF resources for PowerShell to peruse, most of my sources are C# so there could be something I am missing in platform difference. I attempted using streams, as used here. I have also referenced MSDN's Example, Bob Powell's example, and every question here for "A generic error occurred in GDI+".
I have been able to get just about every approach to work for the first frame (page) but I always get the same error on additional pages.
Here is the code (save it in a folder with a multipage TIFF and execute):
Function Test-Image{
[cmdletbinding()]
param(
)
#Image Resolution in DPI
$ImageRes = 200
$ImageQuality = 100 # %
$ImageBitDepth = 1 # ColorDepth
$FileObjects = Get-ChildItem -File -Recurse -Exclude "Test-Image.ps1"
$ImageFiles = ($FileObjects | Where-Object {".bmp",".gif",".jpg",".jpeg",".png",".tif",".tiff",".wmf" -eq $_.Extension -and !$_.PSIsContainer -and $_.fullname -notlike "$DropBoxPath\UnsupportedFiles\*"}).fullname
#Process Image Files
$imageCodec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() |Where-Object {$_.MimeType -eq "image/tiff"}
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(3)
$encoderParams.Param[1] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, $ImageQuality)
$encoderParams.Param[2] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::ColorDepth, $ImageBitDepth)
foreach ($ImageFile in $ImageFiles) {
#Load the Image
$SourceImage = New-Object System.Drawing.Bitmap($ImageFile) -EV e
If ($e.Count -eq 0) {
#Generate Random File Name
$NewFileName = "$([System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName())).tif"
$FrameCount = $SourceImage.GetFrameCount($SourceImage.FrameDimensionslist[0])
# Make sure we start on the First Frame (Page)
$SourceImage.SelectActiveFrame($SourceImage.FrameDimensionsList[0],0) | Out-Null
#Create the New Tiff and load the first Frame.
$NewImage = New-Object System.Drawing.Bitmap($SourceImage)
$NewImage.setResolution($ImageRes,$ImageRes)
for ($FrameIndex=0; $FrameIndex -lt $FrameCount; $FrameIndex++) {
write-host "Processing $ImageFile Page $($FrameIndex + 1) of $FrameCount" #| Out-File -Append -FilePath $LogFile
If ($FrameIndex -eq 0) {
#First Page
# Set Save Flag to MultiFrame
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag,"MultiFrame")
# Save the First Frame (Page)
$NewImage.Save("$([System.IO.Path]::GetDirectoryName($ImageFile))\$NewFileName",$imageCodec,$encoderParams)
} Else {
#Additional Pages
# Set the Current Frame
$SourceImage.SelectActiveFrame($SourceImage.FrameDimensionsList[0],$FrameIndex) | Out-Null
# Switch Save Flag to add Frame
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag,"FrameDimensionPage")
$NewImage.SaveAdd($SourceImage,$encoderParams)
}
}
# Cleanup
$SourceImage.Dispose()
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag,"Flush")
$NewImage.SaveAdd($encoderParams)
$NewImage.Dispose()
#Remove-Item -Path $ImageFile -Force
} Else {
write-host "$ImageFile Could not be opened." | Out-File -Append -FilePath $LogFile
}
}
#Clear Variable
Remove-Variable ImageFiles
}
Test-Image
Here are the errors, which I'm assuming are caused by the same problem:
Exception calling "SaveAdd" with "2" argument(s): "A generic error occurred in GDI+."
At V:\jshaw\My Pictures\test\Test-Image.ps1:48 char:6
+ $NewImage.SaveAdd($SourceImage,$encoderParams)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ExternalException
Exception calling "SaveAdd" with "1" argument(s): "A generic error occurred in GDI+."
At V:\jshaw\My Pictures\test\Test-Image.ps1:54 char:4
+ $NewImage.SaveAdd($encoderParams)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ExternalException
I have now tried executing the following C# code from within my PowerShell script with the same error. Please bear in mind that this is my first attempt at C#. I just altered the MSDN example to suit my needs.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace jshaw
{
public class MultiFrame_Tiff
{
public static void Process_Tiff(string ImageFile)
{
Bitmap SourceImage;
Bitmap NewImage;
ImageCodecInfo myImageCodecInfo;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
// Create three Bitmap objects.
SourceImage = new Bitmap(ImageFile);
SourceImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page,0);
NewImage = new Bitmap(SourceImage);
// Create New File Name
string NewFileName = Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".tif";
// Get an ImageCodecInfo object that represents the TIFF codec.
myImageCodecInfo = GetEncoderInfo("image/tiff");
// Create an Encoder object based on the GUID
// for the SaveFlag parameter category.
myEncoder = Encoder.SaveFlag;
// Create an EncoderParameters object.
// An EncoderParameters object has an array of EncoderParameter
// objects. In this case, there is only one
// EncoderParameter object in the array.
myEncoderParameters = new EncoderParameters(1);
// Save the first page (frame).
myEncoderParameter = new EncoderParameter(myEncoder,(long)EncoderValue.MultiFrame);
myEncoderParameters.Param[0] = myEncoderParameter;
NewImage.Save(NewFileName, myImageCodecInfo, myEncoderParameters);
int FrameCount = SourceImage.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
for (int i = 1; i < FrameCount;i++)
{
SourceImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page,FrameCount);
// Save the second page (frame).
myEncoderParameter = new EncoderParameter(myEncoder,(long)EncoderValue.FrameDimensionPage);
myEncoderParameters.Param[0] = myEncoderParameter;
NewImage.SaveAdd(SourceImage, myEncoderParameters);
}
// Close the multiple-frame file.
myEncoderParameter = new EncoderParameter(myEncoder,(long)EncoderValue.Flush);
myEncoderParameters.Param[0] = myEncoderParameter;
NewImage.SaveAdd(myEncoderParameters);
}
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
{
if(encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
}
}