C# .NET "Parameter is invalid" when Image in using statement

767 Views Asked by At

Windows 8.1 Pro, Visual Studio 2015 Update 3, C#, .NET Framework 4.5. Ghostscript.NET (latest), GhostScript 9.20.

I'm converting a PDF to a PDF. Hah. Well, I'm making an "editable" PDF "hard" PDF that can't be edited and is of lower quality. The process is I take the editable PDF, save it out as x-pages of PNG files, convert those PNG files to a multipage TIFF, and then convert the multipage TIFF to the PDF I need.

This worked just fine with Visual Studio 2012, one version earlier of GhostScript .NET and GS 9.10.

public static Tuple<string, List<string>> CreatePNGFromPDF(string inputFile, string outputfile)
{
    Tuple<string, List<string>> t = null;
    List<string> fileList = new List<string>();
    string message = "Success";
    string outputFileName = string.Empty;

    int desired_x_dpi = 96;
    int desired_y_dpi = 96;

    try
    {
        using (GhostscriptViewer gsViewer = new GhostscriptViewer())
        {
            gsViewer.Open(inputFile);
            using (GhostscriptRasterizer rasterizer = new GhostscriptRasterizer(gsViewer))
            {
                for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
                {
                    using (System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber))
                    {
                        outputFileName = outputfile.Replace(".png", string.Empty) + "_page_" + pageNumber.ToString() + ".png";
                        img.Save(outputFileName, ImageFormat.Png);
                        if (!fileList.Contains(outputFileName))
                        {
                            fileList.Add(outputFileName);
                        }
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        message = ex.Message;
    }

    t = new Tuple<string, List<string>>(message, fileList);
    return t;
}

This now fails on this line:

using (System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber))

when processing the second page. The first page works okay.

I downloaded the source for GhostScript.NET, added it to my solution, debugged, etc., and spent a good long while trying to figure this out.

I then decided to separate out the functionality and make the bare minimum available for me to examine further in a simple Console application:

static void Main(string[] args)
{
    int xDpi = 96;
    int yDpi = 96;

    string pdfFile = @"Inputfilenamehere.pdf";
    GhostscriptVersionInfo gsVersionInfo = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL | GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
    List<GhostscriptVersionInfo> gsVersionInfoList = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL);

    try
    {
        using (GhostscriptViewer gsViewer = new GhostscriptViewer())
        {
            gsViewer.Open(pdfFile);
            using (GhostscriptRasterizer gsRasterizer = new GhostscriptRasterizer(gsViewer))
            {
                int pageCount = gsRasterizer.PageCount;

                for (int i = 0; i < pageCount; i++)
                {
                    Image img = gsRasterizer.GetPage(xDpi, yDpi, i + 1);
                }
            }
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

Lo and behold, no problems. The difference is that I'm not putting declaration of my Image in the using statement.

I always try to be a good boy developer and use a using statement whenever the class implements IDisposable.

So, I removed the use of the using and I get the lower-quality PDF's that I've always desired. My life is good now.

        using (GhostscriptViewer gsViewer = new GhostscriptViewer())
        {
            gsViewer.Open(inputFile);
            using (GhostscriptRasterizer rasterizer = new GhostscriptRasterizer(gsViewer))
            {
                for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
                {
                    System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber);

                    outputFileName = outputfile.Replace(".png", string.Empty) + "_page_" + pageNumber.ToString() + ".png";
                    img.Save(outputFileName, ImageFormat.Png);
                    if (!fileList.Contains(outputFileName))
                    {
                        fileList.Add(outputFileName);
                    }
                }
            }
        }

Note that if I call img.Dispose() at the end of the for loop, I get the same error again!

My best guess is that my issue is not a GhostScript or GhostScript.NET issue. Am I being a bonehead for insisting on blindly using "using" statements if the class implements IDisposable? I've always understood that it's best practice to wrap anything that implements IDisposable with a using statement to forgo leaks, etc.

Hence, my question: Any ideas why I get the "Parameter is invalid" exception when I initialize the System.Drawing.Image class within the using statement but not when I don't? I'd love to understand this more.

Better yet, if anyone knows how I can get this functionality and also ensure I'm properly disposing my object, that would be the best.

I didn't find much about this particular topic when I searched for information. I did find one other StackOverflow post about someone using a graphic object in a using statement with the same error. I wonder if there is a relationship. I also note that I should be using Dispose(), but that appears to be causing the problem, and I need this to work.

FYI, for anyone interested, the actual error occurs here in GhostscriptInterprester.cs in the GhostScript.NET code:

Method: public void Run(string str) str is "Page pdfshowpage_init pdfshowpage_finish"

// GSAPI: run the string
int rc_run = _gs.gsapi_run_string(_gs_instance, str, 0, out exit_code);
1

There are 1 best solutions below

0
On

I found the root cause of my failure at least. My GhostscriptRasterizer object had a value of '0' set for the height points and width points.

var rasterizer = new GhostscriptRasterizer();
rasterizer.CustomSwitches.Add("-dDEVICEWIDTHPOINTS=" + widthPoints);
rasterizer.CustomSwitches.Add("-dDEVICEHEIGHTPOINTS=" + heightPoints);

Once I set both height and width to a valid non-zero value, the issue got fixed.