简体   繁体   中英

Trouble with multipage TIFF using powershell - SaveAdd “A Generic error occurred in GDI+.”

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;
        }
    }
}

So apparently there something wrong with the implementation of the "EncoderParameter(Encoder, String)" Constructor. Changing those lines to use the "EncoderParameter(Encoder, Int)" Constructor does the trick.

So the following lines:

$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag,"MultiFrame")
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag,"FrameDimensionPage")
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag,"Flush")

Should be this instead:

$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag, [int][System.Drawing.Imaging.EncoderValue]::MultiFrame)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag, [int][System.Drawing.Imaging.EncoderValue]::FrameDimensionPage)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::SaveFlag, [int][System.Drawing.Imaging.EncoderValue]::Flush)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM