Gup3rSuR4c
Gup3rSuR4c

Reputation: 9488

PowerShell and GhostScript

I have a batch script that loops over a folder and flattens the PDFs inside it using GhostScript (9.07). I want to convert it to a PowerShell script because it seems to like to crash every once in a while and I'm just tired of debugging batch files.

Anyway, I have a PS script that seems to work, based on it's output in the console, but I don't actually get any files. Manual entry in PS seems to work even less... What am I doing wrong?

Batch Script

@ECHO OFF
::
:: Process all PDFs and flatten them to PDF/A format
:: =====================================================================================
FOR %%F IN ("J:\Finals\*.pdf") DO (
    IF /I %%F NEQ "*Floor Plan*.pdf" (
::      Convert the original PDF to a flattened PDF PDF/A
::      ========================================================================
        "%ProgramFiles%\gs\gs9.07\bin\gswin64.exe" -dPDFA -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="J:\Finals\%%~nF (Final).pdf" "%%F"
::
::      Delete the original PDF file
::      ========================================================================
        DEL "%%F" /F /Q
::
::      Rename the flattened PDF PDF/A to the original PDF's name
::      ========================================================================
        MOVE "J:\Finals\%%~nF (Final).pdf" "%%F"
    )
)
::
:: Move files form the Readdle drive to the Digital Documents drive
:: =====================================================================================
ROBOCOPY J:\Finals\ K:\ *.* /MOV /R:0 /W:0 /MT

PowerShell Script (so far)

$GhostScript = "$env:ProgramFiles\gs\gs9.07\bin\gswin64c.exe"

Get-ChildItem "C:\Test In\*.pdf" | Where {
    $_.BaseName -NotMatch "Floor Plan"
} | ForEach-Object {
    $InputFile = $_.FullName
    $OutputFile = "C:\Test Out\{0} (Final).pdf" -F $_.BaseName

    & "$GhostScript" -dPDFA -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="$OutputFile" "$InputFile"
}

Output of the PowerShell Script

GPL Ghostscript 9.07 (2013-02-14)
Copyright (C) 2012 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Processing pages 1 through 1.
Page 1
Substituting font Times-Italic for TimesNewRomanPS-ItalicMT.
Loading NimbusRomNo9L-ReguItal font from %rom%Resource/Font/NimbusRomNo9L-ReguItal... 4198200 2870566 3665244 2332637 3 done.
Substituting font Courier for CourierNewPSMT.
Loading NimbusMonL-Regu font from %rom%Resource/Font/NimbusMonL-Regu... 3755680 2306439 4089108 2566088 3 done.
Loading NimbusRomNo9L-Regu font from %rom%Resource/Font/NimbusRomNo9L-Regu... 3796376 2391131 4078352 2484871 3 done.
Loading Dingbats font from %rom%Resource/Font/Dingbats... 3917480 2509851 4280192 2689988 3 done.

For anyone who cares this is being tested on a Windows Server 2008 R2 virtual machine. It has 4GB of RAM and 4vCPUs. I've also checked if there was permissions issues with the test folders I was using and it wasn't the case.

UPDATE

I've updated my post to show what the current script looks like. Surrounding the $OutputFile variable in quotes doesn't do anything. Regardless of quotes or not, I always get output (which I've added above) that indicates the GhostScript is doing what it's supposed to, but ultimately I get no file created...

Upvotes: 2

Views: 8174

Answers (5)

hsbatra
hsbatra

Reputation: 143

I know that this question is quite old - but here's my take on the situation.

Was the Batch Script tried out by itself to see whether any output file was produced - if not, it may be that delete file command in the batch script ran before the GhostScript executable had finished executing.

To resolve the issue, one could try one of the following two approaches

  1. Add a delay between the ghostscript invocation and the delete command via the timeout command

    timeout /t 3 /nobreak > nul

will add a 3 second delay. Adjust it to suit your requirements.

  1. Add a "start" command to the beginning of the ghostscript invocation

    start /wait /min "" "%ProgramFiles%\gs\gs9.07\bin\gswin64.exe" ...

The /wait switch will cause the del command to be executed only after the target of the start has completed. The /min switch will suppress the flash of the console window. The empty string "" after the /min switch is necessary to avoid the start command from treating the path to the ghostscript executable as a console title.

Additionally, replacing the ghostscript arguments with

-q -dNOPAUSE -dBATCH -dSAFER -dSimulateOverprint=true -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=150 -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=150 -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=150 

will probably result in a much smaller pdf being generated than the minimal settings being used in the original post.

Upvotes: 0

js2010
js2010

Reputation: 27626

This is actually odd. I looked at this with process monitor. Ghostscript actually outputs to '$Outputfile' literally, when not surrounded by double quotes.

 & $GhostScript -dPDFA -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="$OutputFile" $InputFile

This was sufficient for me, even with spaces in the $ghostscript and $inputfile variables. It looks like powershell doesn't normally interpret variables in what looks like a -parameter unless a colon preceeds it, or it's double quoted, even in PS 6.

Simpler demo:

$a = 'hi'

echo -InputObject$a

-InputObject$a

echo -InputObject"$a"

-InputOjbecthi

echo -InputObject:$a

hi

Upvotes: 0

VolrathTheFallen
VolrathTheFallen

Reputation: 157

For me, using PDFtk generated a bigger PDF than the original, so that wasn't very helpful. I wanted to use powershell too, because it's just the way to go these days, but was having trouble executing ghostscript in powershell too. Since it ran fine when using a batch file, I opted to run the ghostscript using a batch file with two parameters that I call in my powershell script.

A slimmed down version (without the looping and cleanup) looks like this:

$script = "C:\path\to\script.cmd"
$InputFile = "C:\path\to\input.pdf"
$OutputFile = "C:\path\to\output.pdf"
& $script $InputFile $OutputFile

the batch script looks like this:

echo off
set arg1=%1
set arg2=%2

"C:\Program Files\gs\gs9.19\bin\gswin64c.exe" -sDEVICE=pdfwrite -dPDFSETTINGS=/screen -dCompatibilityLevel=1.4 -dNOPAUSE -dBATCH -sOutputFile=%arg2% %arg1%

Upvotes: 1

Gup3rSuR4c
Gup3rSuR4c

Reputation: 9488

Well, I figured out a solution. It involves opting out of using GhostScript for PDFtk. Seriously, I have no idea what GhostScript's deal was, but it just refused to work 100%. It worked like 95%, but the last 5% I really cared about (the outputted file) never happened. Anyway, I instead used PDFtk and it just works, and it also works many times faster than GhostScript for the exact same task. I don't get it...

Here's what my final PowerShell script looks like:

$PDFtk = "C:\Program Files (x86)\PDF Labs\PDFtk Server\bin\pdftk.exe"

If (Test-Path $PDFtk) {
    Get-ChildItem "J:\Finals\" | Where-Object {
        $_.Extension -Match "pdf"
    } | ForEach-Object {
        $InputFile = $_.FullName
        $OutputFile = "J:\Finals\{0} (Flattened).pdf" -F $_.BaseName
        $DigitalDocumentsFile = "K:\{0}" -F $_.Name

        & $PDFtk $InputFile OUTPUT $OutputFile FLATTEN

        If (Test-Path $OutputFile) {
            Remove-Item $InputFile
            Move-Item $OutputFile $DigitalDocumentsFile
        }
    }
}

Upvotes: 1

zdan
zdan

Reputation: 29480

Your $OutFile variable generates a path that has spaces in it, you will need to surround it by quotes:

& $GhostScript -dPDFA -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="$OutputFile" $InputFile

Upvotes: 0

Related Questions