Table of Contents
MakePDF
Please see for the source code below.
Installation
The program has been written for and tested on a Microsoft Windows XP system with the script language autoit3. In order to function it needs to cooperate with a few other applications, which need to be installed as well. These are Ghostscript and ImageMagick.
NB 1. Microsoft Visual C++ 2008 SP1 should be installed due to incompatibility issues with the script.
NB 2. During installation of ImageMagick you have to enable manually the option “Install ImageMagickObject: OLE Control for VBscript, Visual Basic, and WSH.”
Download and install following files:
- Ghostscript: http://www.auditeon.com/xyz/projects/gs856w32.exe
- Microsoft Visual C++ 2008 SP1: search there for vcredist_x86.exe1)
- ImageMagick: ImageMagick
Once all files have been downloaded and installed, finally download:
- The latest application MakePDF: MakePDF.exe
- crc32: 0x357a0e8b
- md5: 0x040362f61d6c37c369370103727af90d
Usage
Either use drag and drop with your pdf or tiff file(s) onto the exe, or add the file(s) as argument.
Nb. It is possible to run several instances of the same exe file. There will be no conflicts between them. With this you are able to process several files simultaneously, reducing the total processing time -assuming your computer has multiple cores-.
(Program filename) Options
Arguments are normally added on the command line, instead of being added to the program filename. The reason to modify the program name is that it enables having different copies of the same program, each with a different behavior. Since any user interaction would hinder automatic processing, the user can decide during dragging and dropping files what options he needs.
- To specify the resolution, add either -rNNN or -rNNNN to your program name. For example if your exe file is called MakePDF.exe, rename it to MakePDF -r600.exe
- To add automatically empty pages at the end of the document when the amount of pages is not a multiple of 4, add -q to your program name. For example if your exe file is called MakePDF -r600.exe, rename it to MakePDF -r600 -q.exe
Windows command line limitation workaround
When selecting a large amount of tif files, windows may throw following error: “Windows cannot access the specified device, path, or file. You may not have the appropriate permissions to access the item”
This error appears due to limitations in Microsoft Windows (2000, XP and later), which can handle only a limited amount of characters on the command line.
In order to convert a large amount of files, select the first and the last file of this range instead. The range should then be automatically recognized.
History
- Changes in v08.e, 30th of May 2012
- Clean up of -partly- messy code.
- Added sequential file sorting as opposed to alphabetical file sorting which is useful for ranges of tif files which have filenames without leading zeros. If a range of tif files is detected, sequential file sorting will be automatically enabled.
- Command line length limitation (Workaround). If there are too many files as argument, drag/drop only the first and last file of a range of tif files. If software recognizes there is a sequence between these selected files (including possible ScanTailor 1L/2R format), it will take that sequence. There may be gaps between them, as long as basic filename matches with each other. Filenames should be either in the format NAME_nnn.tif or NAME_nnn_(1L|2R).tif format), where nnn can be any number and NAME any name.
- A fix for pages which are portrait but turn into landscape after processing is still pending.
- Changes in v0.8c, 22nd of August 2011
- Changed program name from FlattenANDCompactPDF_A4Size into the more descriptive MakePDF
- Changes in v0.8c, 7th of August 2011
- Added option to specify the output resolution2).
- Added option to automatically add empty pages at the end of the document when the amount of pages is not a multiple of 4. If enabled, it will only add pages when the total amount of pages is at least 3.
- Changes in v0.8b, 29th of July 2011
- Cleaning up code of the main process due to similarity between processing of tiff and pdf.
- Fixed a minor bug with page sizes which were almost a4 and did not caused the algorithm to correct for rasterization rounding errors.
- changed comments in the code.
- Changes in v0.8, 28th of July 2011
- New feature: apart from pdf files, it now also accepts per drag and drop one or more .tif files, which are rescaled and converted so that the resulting file will be a pdf file with exactly a4 page dimensions.
- Changes in v0.7, Februari 2010
- Optimize pdf conversion algorithm to increase speed.
Source code
Please note: the code is as is. No warranty is given, nor any support.
; MakePDF, convert one or more tiff files into compact black & white pdf files per drag and drop. ; Alternatively pdf files with multiple layers can be supplied, which will be ; converted to pdf files with a single layer and black and white color. ; Output size will be exact a4 page(s) ; Copyright (C) Februari 2010, July & August 2011, May 2012 by Marc Nijdam ; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see <http://www.gnu.org/licenses/> ; ; Contact details: http://www.nijdam.de/marc.html ; #requireadmin ; This program should have proper rights to start external applications. Presumably this behaviour requires user admin rights #include <Constants.au3> ; Used for registry constants like REG_SZ, REG_DWORD etc. #include <Process.au3> #include <file.au3> #include <GUIConstantsEx.au3> #include <EditConstants.au3> #include <Math.au3> #Include <String.au3> #Include <Array.au3> Global Const $ThisProgramVersion = "0.8e" ; Current release of this software Global Const $ThisProgramDate = "27-05-2012" ; Release date Global Const $ThisProgramName = "MakePDF" ; full filename without spaces and without exe extension ; Dependencies constants Global Const $Ghostscript_name = "gs\gs" ; unique string to search for in %PATH% whether Ghostscript is installed or not Global Const $Ghostscript_RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\GPL Ghostscript" ; registry path for Ghostscript Global Const $imagemagick_name = "imagemagick" ; unique string to search for in %PATH% whether ImageMagick is installed or not Global Const $imagemagick_RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\ImageMagick\Current" ; registry path for ImageMagick ; Requires Imagemagick installation from http://www.imagemagick.org (See over there. Choose the dynamic, 16 bit images version) ; Path environment variable should be pointing to imagemagick, (default installation will add this to the %PATH% environment variable, which is persistant) ; If not there, this script adds the following temporarily: %PROGRAMFILES%\imagemagick ; ; This program requires Ghostscript, which may be found at http://sourceforge.net/projects/ghostscript/ ; Path environment variable should be pointing to: %PROGRAMFILES%\gs\gs8.xx\bin;%PROGRAMFILES%\gs\gs8.xx\lib ; ; The following solves a R6034 (AutoIt3 related) error: ; http://www.microsoft.com/downloads/details.aspx?familyid=A5C84275-3B97-4AB7-A40D-3802B2AF5FC2 ; ; Nb.: ; Write an environment variable: EnvSet ( "envvariable" [, "value"] ) ; ControlClick($parentWindowName,"ViewPages", ,2, 12, 81) ; $CmdLine[0] = number of parameters ; $CmdLine[1] = first parameter ; Current temporary filename: pdf2tif00000_etcetera ; Optional parameters which can be added at the last part of the filename: ; -q -> This will make sure the pdf file has a multiple of 4 pages (Only if the document contains at ; least 3 pages). This option is a practical way to correct page layout with odd and even pages. ; It adds as many as necessary white pages at the end. For example, if your document consists ; of 7 pages, it will add one empty page at the end. The only necesary thing to do then is ; verify if the white page is at the correct location. A way to check this if page numbers ; appear on the inside instead of the outside of pages. ; -r300 -> Set resolution of your images in the pdf document. If not specified, it will default to 300dpi ; Error constants Global Const $_ERROR_MissingImageMagickObject = -1 Global Const $_ERROR_InputFileIsNotValid = -2 Global Const $_ERROR_ShowHelp = -3 Global Const $_ERROR_PdfImageSizeTooLarge = -4 Global Const $_ERROR_CannotFindGhostscript = -5 Global Const $_ERROR_imagemagickNotInstalled = -6 Global Const $_ERROR_MSVisualCPlusPlus2008RedistNotInstalled = -7 Global Const $_ERROR_PDF_FileInconsistent = -8 ; Gui constants Global Const $Gui_Title = $ThisProgramName Global $nEdit ; program options ; $cmdOptions[$_QuadPages] -> False means no quadruple pages ; $cmdOptions[$_Resolution] -> resolution. All Files and all pages within a batch should have the same resolution. ; Initialize with adding empty pages disabled and a resolution of 300dpi Global Const $_OptionChr = '-' Global Const $_QuadPages = 0 Global Const $_Resolution = 1 Dim $cmdOptions[2] = [ False, _ 300] parseCmdOptions(@ScriptName,$cmdOptions) ; Parse the name of the program, check for 'embedded' commands and reinitialize ; tif and pdf constants Global Const $rastering = 72 ; PDF rastering value Global Const $img_width = ($cmdOptions[$_Resolution]/3)*(2480/100); 2480 for a4, constant in pixels, defining what the page width may be at most when portrait modus is used Global Const $img_height = ($cmdOptions[$_Resolution]/3)*(3508/100) ; 3508 for a4, constant in pixels, defining what the page height may be at most when portrait modus is used Global Const $pdfdims[2] = [String(Int(($rastering*$img_width/$cmdOptions[$_Resolution])+0.5)),String(Int(($rastering*$img_height/$cmdOptions[$_Resolution])+0.5))] ; Array holding pdf rastering sizes a4 should be 595x842 Global Const $bitmaperror = int($cmdOptions[$_Resolution]/300) ; To compensate for apparent errors during rasterization of pdf files with ghostscript, a deviation of 1 pixel per 300 dpi will use some quantization Global Const $tif = ".tif" ; File extensie voor tif files Global Const $ps = ".ps" ; File extensie voor ps files Global Const $pdf = ".pdf" ; File extensie voor pdf files ; program specific constants Global Const $Delay=400 ; Short waiting time necessary to retreive console output. Global Const $tempname = @TempDir & "\" & $ThisProgramName & String(StringFormat("%05s", @AutoItPID )) ; Unique name for temporary files Global Const $QQ = '"' Dim $szDrive, $szDir, $szFName, $szExt Dim $i, $j, $k Dim $Pdf_ListOfFiles2Proces = $tempname & "lst.txt" ; Filename for file with list holding each filename for a single pdf page Dim $Pdf_tmp_FileName = $tempname & "Processed" & $pdf ; ###################################################################################################### ; # main ; ###################################################################################################### CheckAndSetEnv() ; Check dependencies and set environment variables GarbageControl($ThisProgramName) ; Cleanup temporary files in $TempDir, but only if no other instances of this program are running. ; Iterate through all via the command line or per 'drag and drop' supplied files $nEdit = CreateGUI($ThisProgramName) ; Create window with info Dim $TotalPages ; Counted pdf pages for each document Dim $GetFileName Dim $LandscapePages[1] ; Array which holds per page landscape or portrait rotation Dim $PageFitsExact[1] ; Array which holds per page whether it is exactly a4 dimension Dim $FileListArray = GetFileListArray($CmdLine[0]) ; $FileListArray[0] contains the first file, rather the count of elements ; Sort Supplied array with files. If it contains only tiff files and conforms to scantailor pattern, sort sequentially, ; otherwise alphabetically. Also check if first (and thus all the rest as well) file is pdf file. True ; means all the supplied files are pdf. Differentiate below between supplied tiff or pdf files. The ; same code can't be simply used for either tiff or pdf files, since it contains many optimalizations ; specifically for pdf. Const $IsPDF = (AnalyzeAndSortArray($FileListArray) == $PDF) If $isPDF Then For $i = 0 to UBound($FileListArray)-1 $GetFileName = $FileListArray[$i] GUICtrlSetData($nEdit, "###### Processing file " & String($i+1) & " of " & String(UBound($FileListArray)-1) & " ######" & @CRLF, 1) GUICtrlSetData($nEdit, "Current file: " & RemovePath($GetFileName) & @CRLF & @CRLF, 1) GUICtrlSetData($nEdit, "Analyzing document..." & @CRLF, 1) $TotalPages = Number(CountPdfPages($GetFileName,$tempname)) If $TotalPages == 0 Then ; pdf file contains 0 pages (means wrong type of file) GUICtrlSetData($nEdit, "->Found 0 pages, skipping this file." & @CRLF & @CRLF, 1) Else GUICtrlSetData($nEdit, "->Found " & $TotalPages & " page(s)" & @CRLF, 1) ProcessPages($TotalPages, $pdf, $GetFileName, $tempname, $Pdf_ListOfFiles2Proces, $FileListArray, $cmdOptions[$_QuadPages]) EndIf Next Else ; process below tiff files GUICtrlSetData($nEdit, "###### Processing tiff files ######" & @CRLF, 1) $TotalPages = UBound($FileListArray) ; number of suppplied tif files. GUICtrlSetData($nEdit, "->Found " & $TotalPages & " image(s)" & @CRLF, 1) ProcessPages($TotalPages, $tif, $GetFileName, $tempname, $Pdf_ListOfFiles2Proces, $FileListArray, $cmdOptions[$_QuadPages]) EndIf Exit ; ###################################################################################################### ; # Below are functions ; ###################################################################################################### ; ; Receives the amount of arguments, create a list of files to process. As such Array[0] contains the ; number of supplied files and check if this program is invoked through Scite. Func GetFileListArray(Const $count) If (@Compiled <> 0) Then If ($count == 0) Then ShowError($_ERROR_ShowHelp) ; number of parameters EndIf Local $tmp = $CmdLine; Array for temporary storing a copy of $CmdLine _ArrayDelete($tmp,0) ; The first element of $CmdLine contains the size of the array, remove that entry. Return $tmp Else ; When invoking this program from within the Scite compiler, use some preset variables for which file to use Local $dbg=$tif ; Choose either $tif or $pdf If $dbg == $pdf Then Local $FileNames = StringSplit("test_size-error-2481x3507_page.01.pdf",",") ; Local $FileNames = StringSplit("test_GS_NotA4_IM_AlmostA4.pdf,a4_2480x3508BWtest.pdf",",") ; Local $FileNames = StringSplit("test_GS_NotA4_IM_AlmostA4.pdf,a4_2480x3508BWtest.pdf,test_4_pages_oversized_grey.pdf",",") ; Local $FileNames = StringSplit("test_GS_NotA4_IM_AlmostA4.pdf,a4_2480x3508BWtest.pdf,test_4_pages_oversized_grey.pdf,test_size-error-2481x3507.pdf",",") ; Local $FileNames = StringSplit("test_GS_NotA4_IM_AlmostA4.pdf,a4_2480x3508BWtest.pdf,test_4_pages_oversized_grey.pdf,test_size-error-2481x3507.pdf,test_2_pages_landscape.pdf",",") ; Local $FileNames = StringSplit("test_GS_NotA4_IM_AlmostA4.pdf,a4_2480x3508BWtest.pdf,test_4_pages_oversized_grey.pdf,test_size-error-2481x3507.pdf,test_2_pages_landscape.pdf,test_4_pages_oversized_grey.pdf",",") ; Local $FileNames = StringSplit("test_GS_NotA4_IM_AlmostA4.pdf,a4_2480x3508BWtest.pdf,test_4_pages_oversized_grey.pdf,test_size-error-2481x3507.pdf,test_2_pages_landscape.pdf,test_4_pages_oversized_grey.pdf,buggyfile_2pages_instead_of_18pages.pdf",",") Else ; Local $FileNames = StringSplit("0002_bartok_2.tiff,0001_bartok_1.tiff,0003_bartok_3.tiff,0005_bartok_5.tiff,0004_bartok_4.tiff,test_size-error-2481x3507.pdf",",") ; Local $FileNames = StringSplit("0002_bartok_2.tiff,0001_bartok_1.tiff,0003_bartok_3.tiff,0005_bartok_5.tiff,0004_bartok_4.tiff",",") Local $FileNames = StringSplit("0002_bartok_2.tiff,0001_bartok_1.tiff,0003_bartok_3.tiff",",") ; Local $FileNames = StringSplit("0002_bartok_2.tiff,0001_bartok_1.tiff",",") EndIf Return $FileNames ; return amount of files. EndIf EndFunc ; get a list of files from a given directory Func getFileList(ByRef $path) Local $search = FileFindFirstFile($path & "*.*") ; Path includes trailing slash If ($search > -1) Then Local $fileList[1] Local $foundObject Do $foundObject = FileFindNextFile($search) If @error Then ExitLoop If (not isDirectory($path & "\" & $foundObject)) Then _ArrayAdd($fileList,$foundObject) Until False FileClose($search) _ArrayDelete($fileList,0) sortSequential($fileList) ; files are sorted from low to high return $fileList EndIf ShowError("Error, cannot create a directory list from the given file.") EndFunc ; Select from $fileList all files which are sequentially ; between $this[0] and $this[1] ; from which the basepart resembles that of the basepart of an array element Func selectFilesFromList(ByRef $this, ByRef $fileList) Local $inRange = False ; If true, then capture the files which are in sequence. Local $tmp[1] ; Empty array with one element Local $i = 0 Do If (TifFilenameBasePart(RemovePath($this[0])) == TifFilenameBasePart($fileList[$i])) Then If ($fileList[$i] == RemovePath($this[0])) Then ; start from range $inRange = True EndIf If ($inRange) Then _ArrayAdd($tmp,getPath($this[0]) & $fileList[$i]) ; $fileList is extracted without path, so add this. EndIf If ($fileList[$i] == RemovePath($this[1])) Then ; end from range $i = Ubound($fileList) EndIf EndIf $i = $i + 1 Until ($i >= UBound($fileList)) If (UBound($tmp)>1) Then _ArrayDelete($tmp,0) return $tmp Else ShowError("Cannot find matching files in directory which have filenames given by ScanTailor") exit EndIf EndFunc Func parseCmdOptions(Const $cmdstrFull, ByRef $cmdOptions) Local $cmdstr = StripExtension(StringRegExpReplace($cmdstrFull, '[ ]', '')) Local $cmdpos = stringlen($cmdstr) If ($cmdpos>0) Then Do Local $cmds = "" Do $cmdchr = stringmid($cmdstr,$cmdpos,1) If ($cmdchr <> $_OptionChr) Then $cmds = $cmdchr & $cmds $cmdpos = $cmdpos - 1 Until (($cmdpos == 0) Or ($cmdchr == $_OptionChr)) If ($cmdchr == $_OptionChr) Then Switch (StringLower(StringLeft($cmds,1))) Case 'q' If (StringLen($cmds) == 1) Then $cmdOptions[$_QuadPages] = True EndIf Case 'r' If ((StringLen($cmds) == 4) Or (StringLen($cmds) == 5)) Then ; Allow 3 and 4 digits numbers, like 300 or 1200 If (StringRegExp(StringTrimLeft($cmds,1), '[^0-9]') == 0) Then ; Check if there are exclusively digits next to the cmd character $cmdOptions[$_Resolution] = Int(Number(StringTrimLeft($cmds,1))) EndIf EndIf Case Else EndSwitch EndIf Until ($cmdpos == 0) EndIf EndFunc ; Convert accurately a single page from a pdf file into a bitmap tif ; For each page: ; GS_Get_PDF_BoundingBoxes() ; BoundingBox=[lower left x, lower left y, upper right x, upper right y] ; if not available, get [0,0,0,0] ; HiResBoundingBox=[lower left x, lower left y, upper right x, upper right y] ; if not available, get [0,0,0,0] ; When gswin32c outputs error text, indicate with 1, otherwise 0 ; ; A4Quantization=False ; If HiResBoundingBox(lower left x + upper right x, lower left y + upper right y) < BoundingBox(lower left x + upper right x, lower left y + upper right y) Then ; If -1 < Pixel Error < 1 Then ; A4Quantization=True ; EndIf ; Else ; gswin32c -sDEVICE=tiffgray -r300 -dNOPAUSE -dQUIET -dBATCH -dAutoFilterGrayImages=false -dGrayImageFilter=/LZWEncode -dFirstPage=1 -dLastPage=1 -sOutputFile=output.tif input ; IM_Get_Image_Dimension() ; If -1 < Pixel Error < 1 Then ; If IM_Get_PDF_BoundingBox() == 595x842 OR IM_Get_PDF_BoundingBox() == 842x595 Then ; A4Quantization=True ; EndIf ; EndIf ; EndIf ; If A4Quantization=True Then ; gswin32c -sDEVICE=tiffg4 -r300 (-g2480x3508|-g3508x2480) -dMaxStripSize=8192 -dFirstPage=1 -dLastPage=1 -sOutputFile=output.tif input ; EndIf ; Return this_page.pdf Func ExtractPageAsTif(Const $GetFileName, Const $j, Const $tempname) Local $tmpbase = $tempname & "_" & String(StringFormat("%05s", $j)) & "tmp" Local $GSResults[9], $IMDims[2] Local $x, $y, $xx, $yy, $quantize, $command Local $pagedims[2] ; String holding page dimensions. With a4 at 300 dpi, it is 2480x3508, with landscape 3508x2480 $GSResults = GS_Get_PDF_BoundingBoxes($GetFileName, $j); Array holding page dimensions from Ghostscript bbox command and error flag If $GSResults[8] Then ShowError($_ERROR_PDF_FileInconsistent) ; If ghostscript threw an error, maybe not all pages can be retreived. Stopping now is the best option. ; Check for portrait or landscape orientation. If ($GSResults[0] + $GSResults[2]) < ($GSResults[1] + $GSResults[3]) Then $pagedims[0] = $img_width ; page is portrait $pagedims[1] = $img_height Else $pagedims[0] = $img_height ; page is landscape $pagedims[1] = $img_width EndIf ; Check if quantization with 1 pixel necessary If ($GSResults[4] + $GSResults[6]) < ($GSResults[0] + $GSResults[2]) AND _ ; Adding [4] + [6] is not what you would expect when calculating dimensions with coordinates ($GSResults[5] + $GSResults[7]) < ($GSResults[1] + $GSResults[3]) Then ; But either GS or IM is wrong in this. For now it gives accurate results of the page size $x = int($cmdOptions[$_Resolution]/$rastering * ($GSResults[4] + $GSResults[6])+0.5) ; Width in pixels $y = int($cmdOptions[$_Resolution]/$rastering * ($GSResults[5] + $GSResults[7])+0.5) ; height in pixels $xx = _Min($x,$y) ; shortest side of image $yy = _Max($x,$y) ; widest side of image If $xx >= ($img_width-$bitmaperror) AND $xx <= ($img_width+$bitmaperror) AND _ ; 1 pixel error at 300 dpi between 2479 and 2481 pixels. $yy >= ($img_height-$bitmaperror) AND $yy <= ($img_height+$bitmaperror) Then $quantize = True $Command = "gswin32c " & _ ; convert file to tif, Mind that you should correct the pixel aspect ratio, which is not shown properly within PSCS4 "-sDEVICE=tiffg4" & " " & _ "-r" & String($cmdOptions[$_Resolution]) & " " & _ "-g" & String($pagedims[0]) & "x" & String($pagedims[1]) & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-dFirstPage=" & Number($j) & " " & _ "-dLastPage=" & Number($j) & " " & _ "-sOutputFile=" & $QQ & $tmpbase & $tif & $QQ & " " & _ $QQ & $GetFileName & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) EndIf EndIf If Not $quantize Then $Command = "gswin32c " & _ ; convert file to tif. Use grayscale: If size exceeds a4, then additonal high quality resizing will be necessary "-sDEVICE=tiffgray" & " " & _ "-r" & String($cmdOptions[$_Resolution]) & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-dAutoFilterGrayImages=false" & " " & _ "-dGrayImageFilter=/LZWEncode" & " " & _ "-dFirstPage=" & Number($j) & " " & _ "-dLastPage=" & Number($j) & " " & _ "-sOutputFile=" & $QQ & $tmpbase & $tif & $QQ & " " & _ $QQ & $GetFileName & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) EndIf $IMDims = IM_Get_Dims($tmpbase & $tif) If $quantize = False Then $x = $IMDims[0] ; Width in pixels $y = $IMDims[1] ; height in pixels $xx = _Min($x,$y) ; shortest side of image $yy = _Max($x,$y) ; widest side of image If $xx >= ($img_width-$bitmaperror) AND $xx <= ($img_width+$bitmaperror) AND _ ; 1 pixel error at 300 dpi between 2479 and 2481 pixels wide. $yy >= ($img_height-$bitmaperror) AND $yy <= ($img_height+$bitmaperror) Then $quantize = True $Command = "gswin32c " & _ ; convert file to tif "-sDEVICE=tiffg4" & " " & _ "-r" & String($cmdOptions[$_Resolution]) & " " & _ "-g" & String($pagedims[0]) & "x" & String($pagedims[1]) & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-dFirstPage=" & Number($j) & " " & _ "-dLastPage=" & Number($j) & " " & _ "-sOutputFile=" & $QQ & $tmpbase & $tif & $QQ & " " & _ $QQ & $GetFileName & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) Return $pagedims EndIf EndIf Return $IMDims EndFunc ; function to process one or more pages. Will do either pdf or tiff Func ProcessPages(Const $TotalPages, Const $imgType, ByRef $GetFileName, Const $tempname, Const $Pdf_ListOfFiles2Proces, Const $FileListArray, Const $q) If (($q) AND ($TotalPages > 2)) Then GUICtrlSetData($nEdit, "(+) Option enabled to add empty pages. Will add " & CalcEmptyPages($TotalPages) & " page(s) at the end." & @CRLF, 1) local $LandscapePages[$TotalPages], $PageFitsExact[$TotalPages] local $FilePagesLargestDim[2] ; Dimensions of the largest page found in a current file. local $FilePagesProperties[2] ; $FilePagesProperties[0]=width in pixels, $FilePagesProperties[1]=height in pixels $FilePagesLargestDim[0]=0 $FilePagesLargestDim[1]=0 Local $j, $tmp For $j = 1 to $TotalPages If ($imgType == $pdf) Then GUICtrlSetData($nEdit, "-->Extracting page " & String($j) & " out of " & String($TotalPages) & @CRLF, 1) $FilePagesProperties = ExtractPageAsTif($GetFileName, $j, $tempname) ; Convert single pdf page to tif, retreive page dimensions Else GUICtrlSetData($nEdit, "-->processing image " & String($j) & " out of " & String($TotalPages) & @CRLF, 1) $GetFileName = $FileListArray[$j - 1] $FilePagesProperties = IM_Get_Dims($GetFileName) ; get dimensions from tiff file FileCopy($GetFileName,$tempname & "_" & String(StringFormat("%05s", $j)) & "tmp" & $tif) EndIf If $FilePagesProperties[0] < $FilePagesProperties[1] Then ; Keep track of page orientation and largest page dimensions $LandscapePages[$j-1] = False If $FilePagesProperties[0] == $img_width AND $FilePagesProperties[1] == $img_height Then $PageFitsExact[$j-1] = True Else $PageFitsExact[$j-1] = False EndIf Else $LandscapePages[$j-1] = True If $FilePagesProperties[0] == $img_height AND $FilePagesProperties[1] == $img_width Then $PageFitsExact[$j-1] = True Else $PageFitsExact[$j-1] = False EndIf EndIf $FilePagesLargestDim[0] = _Max($FilePagesLargestDim[0],$FilePagesProperties[0]) $FilePagesLargestDim[1] = _Max($FilePagesLargestDim[1],$FilePagesProperties[1]) Next If FileExists($Pdf_ListOfFiles2Proces) Then FileDelete($Pdf_ListOfFiles2Proces) ; Empty this file before adding filenames as text to it. Will be used to merge several pdf files into one. $FHandle = FileOpen($Pdf_ListOfFiles2Proces,1) ; $Pdf_ListOfFiles2Proces is a text file containg file names. (1 = Write mode append to the end of file) Const $ScaleFactor = Calc_scalingfactor($FilePagesLargestDim[0], $FilePagesLargestDim[1], $img_width, $img_height) ; resizing factor to make sure largest page fits within an a4 page. If $ScaleFactor < 100 Then GUICtrlSetData($nEdit, "-->(Some) pages exceed the targeted page output size." & @CRLF & "Resize factor for pages is set to " & String($ScaleFactor) & " percent" & @CRLF, 1) For $j = 1 to $TotalPages ; Resize image GUICtrlSetData($nEdit, "-->Converting page " & String($j) & " out of " & String($TotalPages) & " to pdf" & @CRLF, 1) $tmp = $tempname & "_" & String(StringFormat("%05s", $j)) & "tmp" ConvertScaleTif2Pdf($tmp, $j, $ScaleFactor, $LandscapePages[$j-1]) ; Convert single tif page to pdf, retreive page dimensions FileWriteLine($FHandle,$tmp & $pdf & @CRLF) Next Else For $j = 1 to $TotalPages ; Do not resize image GUICtrlSetData($nEdit, "-->Converting page " & String($j) & " out of " & String($TotalPages) & " to pdf" & @CRLF, 1) $tmp = $tempname & "_" & String(StringFormat("%05s", $j)) & "tmp" ConvertTif2Pdf($tmp, $j, $PageFitsExact[$j-1], $LandscapePages[$j-1]) ; Convert single tif page to pdf, retreive page dimensions FileWriteLine($FHandle,$tmp & $pdf & @CRLF) Next EndIf If (($q) AND ($TotalPages > 2) AND (CalcEmptyPages($TotalPages)>=1)) Then ; If required add empty pages at the end. For $j = 1 to CalcEmptyPages($TotalPages) $tmp = $tempname & "_" & String(StringFormat("%05s", $j+$TotalPages)) & "tmp" ; temporary filename without extension AddEmptyPage($tmp,$LandscapePages[0]) ; use landscape/portrait orientation from first page FileWriteLine($FHandle,$tmp & $pdf & @CRLF) Next EndIf FileClose($FHandle) MergePDF_BW($Pdf_tmp_FileName, $Pdf_ListOfFiles2Proces) If $isPDF Then FileCopy($Pdf_tmp_FileName, UniqueFileName(ModifyFileName($GetFileName))) Else FileCopy($Pdf_tmp_FileName, UniqueFileName(ModifyFileName(StripExtension($FileListArray[0]) & $pdf ))) EndIf FileDelete($Pdf_tmp_FileName) IndirectDelete($Pdf_ListOfFiles2Proces) FileDelete($Pdf_ListOfFiles2Proces) EndFunc ; find the pdf HiResBoundingBox with ghostscript. GS String: gswin32c -sDEVICE=bbox -dQUIET -dNOPAUSE -dBATCH file Func GS_Get_PDF_BoundingBoxes(Const $PDFFile, Const $j) Local $line, $GSResult[9], $Command, $Result = "" $Command = "gswin32c.exe " & _ "-sDEVICE=bbox" & " " & _ ; Ghostview command to get details like HiResBoundingBox "-dQUIET" & " " & _ "-dNOPAUSE"& " " & _ "-dBATCH" & " " & _ "-dFirstPage=" & Number($j) & " " & _ "-dLastPage=" & Number($j) & " " & _ $QQ & $PDFFile & $QQ Local $console = Run(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD) While 1 Sleep($Delay) $line = StderrRead($console) If @error Then ExitLoop $Result = $line WEnd $GSResult[0] = Number(StringRegExpReplace($Result, '(?s)%%BoundingBox:[\s]*([\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)(.*)', '$1')) $GSResult[1] = Number(StringRegExpReplace($Result, '(?s)%%BoundingBox:[\s]*([\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)(.*)', '$2')) $GSResult[2] = Number(StringRegExpReplace($Result, '(?s)%%BoundingBox:[\s]*([\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)(.*)', '$3')) $GSResult[3] = Number(StringRegExpReplace($Result, '(?s)%%BoundingBox:[\s]*([\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)(.*)', '$4')) $GSResult[4] = Number(StringRegExpReplace($Result, '(?s).*HiResBoundingBox:.*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*', '$1')) $GSResult[5] = Number(StringRegExpReplace($Result, '(?s).*HiResBoundingBox:.*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*', '$2')) $GSResult[6] = Number(StringRegExpReplace($Result, '(?s).*HiResBoundingBox:.*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*', '$3')) $GSResult[7] = Number(StringRegExpReplace($Result, '(?s).*HiResBoundingBox:.*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*\s(\d+\.\d+).*', '$4')) $GSResult[8] = StringRegExp($Result, '(?s).*error.*') Return $GSResult EndFunc ; find the image dimensions with imagemagick: identify -format "%[fx:w]x%[fx:h]" Func IM_Get_Dims(Const $GetFileName) Local $line, $this_dims[2], $Result = "" Local $Command = "identify " & _ $QQ & "-format" & $QQ & " " & _ ; Ghostview command to get details like HiResBoundingBox $QQ & "%[fx:w]x%[fx:h]" & $QQ & " " & _ ; ImageMagick command to find page dimensions $QQ & $GetFileName & $QQ Local $console = Run(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD) While 1 Sleep($Delay) $line = StdoutRead($console) If @error Then ExitLoop $Result = $line WEnd $this_dims[0] = Number(StringRegExpReplace($result, "([0-9]+)x([0-9]+)", "$1")) ; From string holding dimensions (for example "1800x1600") extract first number $this_dims[1] = Number(StringRegExpReplace($result, "([0-9]+)x([0-9]+)", "$2")) ; From string holding dimensions (for example "1800x1600") extract second number Return $this_dims EndFunc Func IM_Get_Type(Const $GetFileName) Local $line, $this_dims[2], $Result = "" Local $Command = "identify " & _ $QQ & "-format" & $QQ & " " & _ ; Ghostview command to get details like HiResBoundingBox $QQ & "%m" & $QQ & " " & _ ; ImageMagick command to find page dimensions $QQ & $GetFileName & $QQ Local $console = Run(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD) While 1 Sleep($Delay) $line = StdoutRead($console) If @error Then ExitLoop $Result = $line WEnd If StringLen($Result) >= 3 Then Return "." & StringLower(StringLeft($Result,3)) EndIf Return EndFunc Func ConvertScaleTif2Pdf(Const $tmpbase,Const $j, Const $Scale, Const $LandscapePage) Local $Command, $pagedims[2] $Command = "mogrify " & _ ; convert tif to ps. (Converting directly to pdf would causes accuracy errors, therefore intermediate step with tif2ps is obligatory) $QQ & $tmpbase & $tif & $QQ & " " & _ "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ "-resize " & String($Scale) & "% " & _ $QQ & $tmpbase & $tif & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If $LandscapePage Then ; make sure page with landscape size has proper dimension $pagedims[0] = $img_height $pagedims[1] = $img_width Else $pagedims[0] = $img_width $pagedims[1] = $img_height EndIf $Command = "convert " & _ ; create canvas with a4 size "-size " & String($pagedims[0]) & "x" & String($pagedims[1]) & " " & _ "xc:white" & " " & _ $QQ & $tmpbase & "B" & $tif & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) $Command = "composite " & _ ; combine a4 canvas with extracted (and resized) tif $QQ & $tmpbase & $tif & $QQ & " " & _ "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ "-compose atop" & " " & _ "-gravity Center" & " " & _ "-type Bilevel" & " " & _ "-compress lzw" & " " & _ $QQ & $tmpbase & "B" & $tif & $QQ & " " & _ $QQ & $tmpbase & "C" & $tif & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmpbase & "B" & $tif) Then FileDelete($tmpbase & "B" & $tif) ; Delete temp file If FileExists($tmpbase & $tif) Then FileDelete($tmpbase & $tif) ; Delete temp file $Command = "convert " & _ ; convert tif to ps. (Converting directly to pdf would causes accuracy errors, therefore intermediate step with tif2ps is obligatory) "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ $QQ & $tmpbase & "C" & $tif & $QQ & " " & _ "-page " & String($pagedims[0]) & "x" & String($pagedims[1]) & "+0+0" & " " & _ $QQ & $tmpbase & $ps & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmpbase & "C" & $tif) Then FileDelete($tmpbase & "C" & $tif) ; Delete temp file $Command = "gswin32c " & _ ; convert ps to pdf "-sDEVICE=pdfwrite" & " " & _ "-r" & String($rastering) & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-sOutputFile=" & $QQ & $tmpbase & $pdf & $QQ & " " & _ "-dDEVICEWIDTHPOINTS=" & String(Int(($rastering*$pagedims[0]/$cmdOptions[$_Resolution])+0.5)) & " " & _ "-dDEVICEHEIGHTPOINTS=" & String(Int(($rastering*$pagedims[1]/$cmdOptions[$_Resolution])+0.5)) & " " & _ $QQ & $tmpbase & $ps & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmpbase & $ps) Then FileDelete($tmpbase & $ps) ; Delete temp file EndFunc Func AddEmptyPage(Const $tmp,Const $LandscapePage) Local $Command, $tf, $pagedims[2] If $LandscapePage Then ; make sure page with landscape size has proper dimension $pagedims[0] = $img_height $pagedims[1] = $img_width Else $pagedims[0] = $img_width $pagedims[1] = $img_height EndIf $Command = "convert " & _ ; create canvas with a4 size "-size " & String($pagedims[0]) & "x" & String($pagedims[1]) & " " & _ "xc:white" & " " & _ $QQ & $tmp & "B" & $tif & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) $Command = "convert " & _ ; convert tif to ps. (Converting directly to pdf would causes accuracy errors, therefore intermediate step with tif2ps is obligatory) "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ $QQ & $tmp & "B" & $tif & $QQ & " " & _ "-page " & String($pagedims[0]) & "x" & String($pagedims[1]) & "+0+0" & " " & _ $QQ & $tmp & $ps & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmp & "B" & $tif) Then FileDelete($tmp & "B" & $tif) ; Delete temp file $Command = "gswin32c " & _ ; convert ps to pdf "-sDEVICE=pdfwrite" & " " & _ "-r" & String($rastering) & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-sOutputFile=" & $QQ & $tmp & $pdf & $QQ & " " & _ "-dDEVICEWIDTHPOINTS=" & String(Int(($rastering*$pagedims[0]/$cmdOptions[$_Resolution])+0.5)) & " " & _ "-dDEVICEHEIGHTPOINTS=" & String(Int(($rastering*$pagedims[1]/$cmdOptions[$_Resolution])+0.5)) & " " & _ $QQ & $tmp & $ps & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmp & $ps) Then FileDelete($tmp & $ps) ; Delete temp file EndFunc Func ConvertTif2Pdf(Const $tmpbase, Const $j, Const $PageFitsExactly, Const $LandscapePage) Local $Command, $tf, $pagedims[2] If $LandscapePage Then ; make sure page with landscape size has proper dimension $pagedims[0] = $img_height $pagedims[1] = $img_width Else $pagedims[0] = $img_width $pagedims[1] = $img_height EndIf If $PageFitsExactly Then $Command = "mogrify " & _ ; convert a4 tif file to proper format and compression $QQ & $tmpbase & $tif & $QQ & " " & _ "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ "-type Bilevel" & " " & _ "-compress lzw" & " " & _ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) $tf = $tmpbase Else $Command = "convert " & _ ; create canvas with a4 size "-size " & String($pagedims[0]) & "x" & String($pagedims[1]) & " " & _ "xc:white" & " " & _ $QQ & $tmpbase & "B" & $tif & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) $tf = $tmpbase & "C" $Command = "composite " & _ ; combine a4 canvas with extracted (and resized) tif $QQ & $tmpbase & $tif & $QQ & " " & _ "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ "-compose atop" & " " & _ "-gravity Center" & " " & _ "-type Bilevel" & " " & _ "-compress lzw" & " " & _ $QQ & $tmpbase & "B" & $tif & $QQ & " " & _ $QQ & $tf & $tif & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmpbase & "B" & $tif) Then FileDelete($tmpbase & "B" & $tif) ; Delete temp file If FileExists($tmpbase & $tif) Then FileDelete($tmpbase & $tif) ; Delete temp file EndIf $Command = "convert " & _ ; convert tif to ps. (Converting directly to pdf would causes accuracy errors, therefore intermediate step with tif2ps is obligatory) "-density " & String($cmdOptions[$_Resolution]) & " " & _ "-units PixelsPerInch" & " " & _ $QQ & $tf & $tif & $QQ & " " & _ "-page " & String($pagedims[0]) & "x" & String($pagedims[1]) & "+0+0" & " " & _ $QQ & $tmpbase & $ps & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tf & $tif) Then FileDelete($tf & $tif) ; Delete temp file $Command = "gswin32c " & _ ; convert ps to pdf "-sDEVICE=pdfwrite" & " " & _ "-r" & String($rastering) & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-sOutputFile=" & $QQ & $tmpbase & $pdf & $QQ & " " & _ "-dDEVICEWIDTHPOINTS=" & String(Int(($rastering*$pagedims[0]/$cmdOptions[$_Resolution])+0.5)) & " " & _ "-dDEVICEHEIGHTPOINTS=" & String(Int(($rastering*$pagedims[1]/$cmdOptions[$_Resolution])+0.5)) & " " & _ $QQ & $tmpbase & $ps & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) If FileExists($tmpbase & $ps) Then FileDelete($tmpbase & $ps) ; Delete temp file EndFunc ; merge pdf pages into one pdf page and deletes intermediate files Func MergePDF_BW(Const $Pdf_tmp_FileName, Const $Pdf_ListOfFiles2Proces) Local $Command = "gswin32c " & _ ; Merges pdf pages into one pdf file "-sDEVICE=pdfwrite" & " " & _ "-dBATCH" & " " & _ "-dNOPAUSE" & " " & _ "-dQUIET" & " " & _ "-sOutputFile=" & $QQ & $Pdf_tmp_FileName & $QQ & " " & _ $QQ & "@" & $Pdf_ListOfFiles2Proces & $QQ RunWait(@ComSpec & " /c " & $Command, @ScriptDir, @SW_HIDE) EndFunc ; Find scaling factor to a4 Func Calc_scalingfactor(Const $x1, Const $y1, Const $img_width, Const $img_height) Local $Scale = 1 If _Min($x1,$y1) > $img_width Then $Scale = $img_width/_Min($x1,$y1) EndIf If _Max($x1,$y1) > $img_height Then $Scale = _Min($img_height/_Max($x1,$y1),$Scale) EndIf Return Int($Scale*100) ; Scalingfactor is between 0 and 100; 100 means no change. EndFunc Func CalcEmptyPages($count) Return Abs(BitAND(mod($count+3,4),3)-3) EndFunc ; Extract file name from path Func RemovePath(Const $OutputFile) If FileExists(RemoveDoubleQuotes($OutputFile)) Then Local $szDrive, $szDir, $szFName, $szExt _PathSplit(RemoveDoubleQuotes($OutputFile), $szDrive, $szDir, $szFName, $szExt) Return $szFName & $szExt Else ShowError("Cannot find working directory") EndIf EndFunc ; Remove filename from path Func getPath(Const $OutputFile) If FileExists(RemoveDoubleQuotes($OutputFile)) Then Local $szDrive, $szDir, $szFName, $szExt _PathSplit(RemoveDoubleQuotes($OutputFile), $szDrive, $szDir, $szFName, $szExt) Return $szDrive & $szDir Else ShowError("Cannot find working directory") EndIf EndFunc ; This function will remove a trailing slash to a windows file path Func RemoveDoubleQuotes(Const $var) Return StringRegExpReplace($var, "^""(.*)""$","$1") EndFunc Func ModifyFileName(Const $GetFileName) Local $szDrive, $szDir, $szFName, $szExt _PathSplit($GetFileName, $szDrive, $szDir, $szFName, $szExt) Return $szDrive & $szDir & $szFName & "_Processed" & $szExt EndFunc ; Prevent overwriting a file, when it already exists. This function will find the 'first' free filename. Func UniqueFileName(Const $OutputFile) If FileExists(RemoveDoubleQuotes($OutputFile)) Then Local $i = 1 Local $szDrive, $szDir, $szFName, $szExt _PathSplit(RemoveDoubleQuotes($OutputFile), $szDrive, $szDir, $szFName, $szExt) While FileExists($szDrive & $szDir & $szFName & "(" & String($i) & ")" & $szExt) $i += 1 WEnd Return $szDrive & $szDir & $szFName & "(" & String($i) & ")" & $szExt Else Return $OutputFile EndIf EndFunc ; This function will add a trailing slash to a windows file path Func AddSlash(Const $var) Return StringRegExpReplace($var, "(.)\\*$","$1\\") EndFunc ; Create GUI window with console like output Func CreateGUI(Const $ThisProgramName) Const $Gui_Xpos = 140 Const $Gui_Ypos = 23 Const $Gui_Width = 518 Const $Gui_Height = 200 Const $Gui_Border = 15 Const $Gui_HeightButton = 20 Const $Gui_WidthButton = 75 Const $Gui_WidthSlider = 20 Opt("GUIOnEventMode", 1); GUI should be in OnEvent Mode, otherwise our cancelbutton event is not captured HotKeySet("{Esc}", "CancelButton"); Pressing the Escape button cancels operation GUICreate($Gui_Title,$Gui_Width,$Gui_Height,$Gui_Xpos,$Gui_Ypos) ; will create a dialog box that when displayed is centered $nEdit = GUICtrlCreateEdit ("",$Gui_Border,$Gui_Border,$Gui_Width-$Gui_Border-$Gui_WidthSlider,$Gui_Height-$Gui_Border-2*$Gui_HeightButton, BitOr($ES_AUTOVSCROLL,$ES_READONLY,$ES_MULTILINE)) GUICtrlSetBkColor($nEdit, 0xffffff) $hButton = GUICtrlCreateButton ("Cancel", ($Gui_Width-$Gui_WidthButton)/2,$Gui_Height-$Gui_HeightButton*1.5, $Gui_WidthButton, $Gui_HeightButton) GUICtrlSetOnEvent($hButton, "CancelButton") ; Function executed when cancel button is pressed GUISetState() Return $nEdit EndFunc ; Strip path and extension Func StripExtension(Const $OutputFile) Local $szDrive, $szDir, $szFName, $szExt _PathSplit(RemoveDoubleQuotes($OutputFile), $szDrive, $szDir, $szFName, $szExt) Return $szDrive & $szDir & $szFName EndFunc ; Strip everything but the file extension Func GetExtension(Const $OutputFile) If FileExists(RemoveDoubleQuotes($OutputFile)) Then Local $szDrive, $szDir, $szFName, $szExt _PathSplit(RemoveDoubleQuotes($OutputFile), $szDrive, $szDir, $szFName, $szExt) Return StringLower($szExt) Else ShowError("Cannot find working directory") EndIf EndFunc Func IndirectDelete(Const $Pdf_ListOfFiles2Proces) Local $Handle = FileOpen($Pdf_ListOfFiles2Proces, 0) Local $line While 1 $line = FileReadLine($Handle) If @error = -1 Then ExitLoop If FileExists($line) Then FileDelete($line) ; Delete temp file Wend FileClose($Handle) EndFunc ; Find number of PDF pages in document Func CountPdfPages(Const $GetFileName, Const $tempname) Local $Command $Command = $QQ & $GetFileName & $QQ & " " & _ ; Use pdf2dsc to find out the number of pages. E.g. %%Pages: 1 $QQ & $tempname & "info.dsc" & $QQ _RunDos("pdf2dsc " & $Command) $Command = OpenFileAndGrep($tempname & "info.dsc", "%%Pages:") If FileExists($tempname & "info.dsc") Then FileDelete($tempname & "info.dsc") ; Delete dsc file If Not $Command Then Return(0); The supplied file is not a pdf file, in order to skip this file, 0 (pages) will be returned Return(Number(StringReplace($Command, "%%Pages: ", ""))) EndFunc ; Check if first array item is a tiff file, if yes, check all other files for validity as well. If all are tiff files, sort them. ; Sort tif files only on filename, without path ; ---> ByRef Array[] ; filenames including full path, unsorted and unchecked for filetype ; <--- FileExtension ; Extension of series of files Func AnalyzeAndSortArray(ByRef $this) Local $FilesArePDF = True ; True means all extensions are pdf Local $FilesAreTIF = True ; True means all extensions are tif Local $i = 0 ; index variable going through array elements. Do ; Check al file extensions. A series of files should be either tif or pdf If ($FilesAreTIF == True) Then If (StringRegExp(StringLower($this[$i]),'.*(tif|tiff)$') == 0) Then $FilesAreTIF = False EndIf EndIf If ($FilesArePDF == True) Then If (StringRegExp(StringLower($this[$i]),'.*pdf$') == 0) Then $FilesArePDF = False EndIf EndIf $i = $i + 1 Until ($i > UBound($this)-1) ; A filetype test will only be done with checking the extension for .tif or .tiff filetype. If ($FilesAreTIF) Then If (UBound($this) == 2) Then If (CompareTwoFiles($this)) Then sortSequential($this) ; file with lowest index is first element $this = selectFilesFromList($this,getFileList(getPath($this[0]))) ; Overwrite the array with a list from files between first and last selected GUICtrlSetData($nEdit, "->Using range of files between the two selected ones." & @CRLF, 1) return $TIF EndIf EndIf If (AreFilesSequential($this)) Then sortSequential($this) GUICtrlSetData($nEdit, "->Enabled sequential ordering of files instead of alphabetical." & @CRLF, 1) Else _ArraySort($this) EndIf Return $TIF ElseIf ($FilesArePDF) Then _ArraySort($this) Return $PDF Else ShowError("Not all files are of the same type. Please supply either only pdf or only tif files.") Return -1 EndIf EndFunc ; Verify if the supplied files have sequential numbering Func AreFilesSequential(ByRef $this) Local $i = 0 Local $FileCriteria = True ; Criteria are true if: valid scantailor output filename format: '.*_\d+\.([^.]*)ti(f|ff)$' or '.*_\d+_(1L|2R)\.([^.]*)ti(f|ff)$' if (UBound($this) <= 1) Then Return False ; No sorting with no or only one element. Else Local $pnumList[UBound($this)] ; Array holding all page numbers. If this array contains two or more of the same entry, files are not sequential Do $pnumList[$i] = find_tif_PgNr($this[$i]) If ($pnumList[$i] == -1) Then ; Check if fileformat criteria are valid for sorting sequentially $FileCriteria = False EndIf $i = $i + 1 Until (($i > UBound($this)-1) Or ($FileCriteria == False)) ; stop after last item or if wrong filename appears EndIf If ($FileCriteria) Then _Arraysort($pnumList) ; Check if all page numbers are really unique Local $isDifferent = True Do If ($pnumList[$i-1] == $pnumList[$i-2]) Then $isDifferent = False EndIf $i = $i - 1 Until (($i <= 1) Or ($isDifferent == False)) Return $isDifferent ; true if all pagenumbers are unique and false if there are two pages with the same number Else Return False EndIf EndFunc ; Analyze path, return True if it is a directory Func isDirectory($this) If (StringInStr(FileGetAttrib($this),"D") == 0) Then Return False ; $this is not a directory Return True ; $this is a directory EndFunc ; Function to extract the number which comes after the underscore ; in a string, immediatelly followed by a dot and the tif filetype. ; This will cause a problem with filenames with a trailing _1L or _2R ; Solution: If a filename is recognized with a trailing _1L or _2R it will multiply the pagenumber with 2, add 0 or 1, depending on 1L or 2R. ; "Cb_1.tif" will return 1 Func find_tif_PgNr(Const $tmp) If (StringRegExp(StringLower($tmp),'.*_\d+\.([^.]*)ti(f|ff)$') == 1) Then Return Number(StringRegExpReplace($tmp, '.*_([\d]+)(.*)', '$1')) ElseIf (StringRegExp(StringLower($tmp),'.*_\d+_(1l|2r)\.([^.]*)ti(f|ff)$') == 1) Then Return Number(StringRegExpReplace($tmp, '.*_([\d]+)_(1L|2R)(.*)', '$1'))*2 + Number(StringRegExpReplace($tmp, '.*_([\d])(L|R)(.*)', '$1')) - 1 Else Return -1 EndIf EndFunc ; This function will sort a one dimensional array which has the ; the following properties: "Text_nnn.tif", where n is a positive number ; in a sequential manner. (In contrast to alphabetically) ; It will return the same array. Func sortSequential(ByRef $this) Local $i ; Index for iterating through array elements Local $SequentiallySorted ; Boolean for informing loop when sorting is finished Do $SequentiallySorted = True For $i=0 to UBound($this)-2 if (find_tif_PgNr($this[$i+1]) < find_tif_PgNr($this[$i])) Then _ArraySwap($this[$i+1], $this[$i]) $SequentiallySorted=False EndIf Next Until ($SequentiallySorted == True) EndFunc ; Compare an array with two elements, each containing a filename and verify if the non-changing part of the name is the same. ; This is useful to check if two files are part of a sequence. Func CompareTwoFiles(ByRef $this) Return (TifFilenameBasePart($this[0]) == TifFilenameBasePart($this[1])) EndFunc ; Extract from a filename the non-changing part. For example: ; Cb_1.tif ----> Cb_.tif ; Cb_1_1L.tif ----> Cb__.tif Func TifFilenameBasePart(ByRef $this) If (StringRegExp(StringLower($this),'.*_\d+\.([^.]*)ti(f|ff)$') == 1) Then Return StringRegExpReplace($this, '(.*_)[\d]+(.*)', '$1$2') ElseIf (StringRegExp(StringLower($this),'.*_\d+_(1l|2r)\.([^.]*)ti(f|ff)$') == 1) Then Return StringRegExpReplace($this, '(.*_)[\d]+(_)(1L|2R)(.*)', '$1$2$4') Else Return -1 EndIf EndFunc ; Opens a file and returns a line which contains a grep alike specified keyword Func OpenFileAndGrep(Const $OFAG_path,Const $OFAG_query) Local $OFAG_file = FileOpen($OFAG_path,0) Local $OFAG_result = FileRead($OFAG_file) FileClose($OFAG_file) If $OFAG_result = "" Then ; If string is empty, return empty string as well Return Else Local $OFAG_q = "" Local $OFAG_stdinArray = StringSplit($OFAG_result, @LF, 1) ; put read data, separated by LineFeed characters, in an array Local $OFAG_i For $OFAG_i In $OFAG_stdinArray If StringInStr($OFAG_i,$OFAG_query) Then $OFAG_q &= $OFAG_i ExitLoop EndIf Next EndIf return $OFAG_q EndFunc ; Cleanup temporary files in $TempDir, but only if no other instances of this program are running. Func GarbageControl(Const $ThisProgramName) Local $i Local $count = 0 Local $list = ProcessList() For $i = 1 to $list[0][0] If $list[$i][0] = $ThisProgramName & ".exe" Then $count = $count+1 Next If $count == 1 Then If FileExists(@TempDir & "\" & $ThisProgramName & "*") Then FileDelete(@TempDir & "\" & $ThisProgramName & "*") ; Delete temporary files EndIf EndFunc ; Check MS dependencies and set environment variables for Imagemagick, Ghostscript Func CheckAndSetEnv() If Not Find_MS_Visual_C_PlusPlus_2008_SP1_Redist() Then ShowError($_ERROR_MSVisualCPlusPlus2008RedistNotInstalled) If Not FileExists(Find_imagemagick_Path("HKEY_LOCAL_MACHINE\SOFTWARE\ImageMagick\Current","BinPath") & "\ImageMagickObject.dll") Then ShowError ($_ERROR_Need_ImageMagickObject_OLE) ; If Not FileExists(@SystemDir & "\ImageMagickObject.dll") Then FileInstall("ImageMagickObject.dll",@SystemDir&"\ImageMagickObject.dll",0) If Not EnvPathExists(Find_Ghostscript_Path($Ghostscript_RegPath,"GS_LIB")) Then SetEnvPath(Find_Ghostscript_Path($Ghostscript_RegPath,"GS_LIB")) ; Check if gs path should be added to %PATH% if it's not there already If Not EnvPathExists(Find_imagemagick_Path($imagemagick_RegPath,"BinPath")) Then SetEnvPath(Find_imagemagick_Path($imagemagick_RegPath,"BinPath")) ; Check if imagemagick path should be added to %PATH% if it's not there already ; If Not ObjectIsRegistered("HKEY_CLASSES_ROOT\ImageMagickObject.MagickImage\CurVer", "") Then RunWait(@ComSpec & " /c regsvr32 /s MagickObject.dll",@SystemDir,@SW_HIDE) EndFunc ; Find Microsoft Visual C++ 2008 SP1 Redistributable Package Func Find_MS_Visual_C_PlusPlus_2008_SP1_Redist() Local $var $var = RegRead("HKEY_CLASSES_ROOT\Installer\Products\D20352A90C039D93DBF6126ECE614057","PackageCode") If @error <> 0 Or $var == "" Then Return False Return True EndFunc ; This function makes sure that the to be added $EnvironmentVariablePath is only added once to the environment path. For this it differentiates between Ghostscript and ImageMagick Func SetEnvPath(Const $EnvVarApplication) If StringRight(EnvGet("PATH"),1) <> ";" Then EnvSet("PATH",EnvGet("PATH") & ";") ; Add a ; separator between path if it's not there already Local $i Local $SplitEnvVarApplication = StringSplit($EnvVarApplication,";") If @error == 1 Then EnvSet("PATH", EnvGet("PATH") & $EnvVarApplication) Else For $i = 1 To $SplitEnvVarApplication[0] If Not StringInStr(EnvGet("PATH"),$SplitEnvVarApplication[$i]) Then EnvSet("PATH", EnvGet("PATH") & $SplitEnvVarApplication[$i] & ";") Next EndIf EndFunc ; This function checks whether the (part of the) $EnvVarApplication already exists in the %PATH% environment. Func EnvPathExists(Const $EnvVarApplication) Local $i Local $found = True Local $SplitEnvVarApplication = StringSplit($EnvVarApplication,";") If @error == 1 Then If StringInStr(EnvGet("PATH"), $EnvVarApplication) Then Return $found ; The delimiter character ; had not been found Else For $i = 1 To $SplitEnvVarApplication[0] If Not StringInStr(EnvGet("PATH"),$SplitEnvVarApplication[$i]) Then $found=False ; One or more applications in the path had been found Next EndIf Return $found EndFunc ; Find Ghostscript Path Func Find_Ghostscript_Path(Const $Ghostscript_RegHive, Const $Ghostscript_EnvKey) Local $var Local $var_read Local $i = 1 Local $GhostscriptVersion Local $GhostscriptPath Local $GhostscriptFound = False Local $GhostscriptPathFound = False While 1 $var = RegEnumKey($Ghostscript_RegHive, $i) If @error <> 0 Then ExitLoop If StringRegExp($var,"^[0-9]+\.[0-9]+") Then $GhostscriptVersion = String($var) $GhostscriptFound = True EndIf $i += 1 WEnd $i = 1 If $GhostscriptFound Then While 1 $var = RegEnumVal($Ghostscript_RegHive & "\" & $GhostscriptVersion, $i) If @error <> 0 Then ExitLoop If $var == $Ghostscript_EnvKey Then $GhostscriptPath = RegRead($Ghostscript_RegHive & "\" & $GhostscriptVersion, $Ghostscript_EnvKey) & ";" $GhostscriptPath &= StringRegExpReplace($GhostscriptPath, "(?i)(.*)(c:\\.*)lib;.*", "$2bin") ; Add also the ghostscript bin path $GhostscriptPathFound = True ExitLoop EndIf $i += 1 WEnd EndIf If $GhostscriptPathFound Then Return $GhostscriptPath Else ShowError ($_ERROR_CannotFindGhostscript) EndIf Return EndFunc ; Find imagemagick Path Func Find_imagemagick_Path(Const $imagemagick_RegHive, Const $imagemagick_EnvKey) Local $var $var = RegRead($imagemagick_RegHive,$imagemagick_EnvKey) If @error <> 0 Or $var == "" Or StringInStr($var,$imagemagick_name) == 0 Then ShowError ($_ERROR_imagemagickNotInstalled) Return $var EndFunc ; Find if object is already registered Func ObjectIsRegistered(Const $Obj_RegHive, Const $Obj_Key) Local $var $var = RegRead($Obj_RegHive,$Obj_Key) If @error <> 0 Or $var <> "ImageMagickObject.MagickImage.1" Then Return False Return True EndFunc ; Cancel button had been pressed. A few cleanup steps should take place. Func CancelButton() GUIDelete() If FileExists($tempname & "*") Then FileDelete(AddSlash(@TempDir) & $tempname & "*") ; Delete temporary files Exit EndFunc ; Show related error, try to terminate program properly Func ShowError(Const $_ERROR_Number) Switch $_ERROR_Number ; Case $_ERROR_MissingImageMagickObject ; MsgBox(0,"Error", "ImageMagickObject cannot be registered." & _ ; "Maybe it is already registered.") Case $_ERROR_InputFileIsNotValid MsgBox(0,"Error", "Supplied file is invalid." & @CRLF & @CRLF & _ "Please check your pdf or tif file.") Case $_ERROR_ShowHelp MsgBox(0,"Help", $Gui_Title & " v" & $ThisProgramVersion & @CRLF & _ "Written by Marc Nijdam, " & $ThisProgramDate & @CRLF & @CRLF & _ "Process one or more pdf/tiff files to black and white pdf files with G4 encoding." & @CRLF & @CRLF & _ "Usage:" & @CRLF & _ StringRegExpReplace(@ScriptName, "(?i)(.*)(\.)(au3|exe)(.*)", "$1$4 ") & "<inputfile1> [<inputfile 2> ... <inputfile n>]"& @CRLF & _ "[...] is optional"& @CRLF & _ "Current resolution is: " & $cmdOptions[$_Resolution] & "dpi" & @CRLF & _ "(filename) options:" & @CRLF & _ " -rNNN or -rNNNN" & @CRLF & _ "Specify resolution NNN or NNNN (in dpi)" & @CRLF & _ " -q" & @CRLF & _ "Add empty pages at the end of the pdf file," & @CRLF & _ "so that the total amount of pages is exactly" & @CRLF & _ "a fourfold of 4." & @CRLF & @CRLF & _ "Ranges of files can also be supplied by selecting only the first and the last inputfile." & @CRLF & _ "All files in between this selection are automatically added." & @CRLF & _ "This offers a workaround for a limitation in windows if a very large amount of tif files" & @CRLF & _ "are selected and windows throws an error, that it cannot access the specified device." & @CRLF & @CRLF & _ "Example: MakePDF-q-r600.exe <file1> <file2> <file3> etc.") Case $_ERROR_PdfImageSizeTooLarge MsgBox(0,"Error", "Image size of PDF file is too large." & @CRLF & @CRLF & _ "Processing would not make much sense.") Case $_ERROR_CannotFindGhostscript MsgBox(0,"Error", "Ghostscript is not installed." & @CRLF & @CRLF & _ "Please go to:" & @CRLF & _ "http://sourceforge.net/projects/ghostscript/" & @CRLF & @CRLF & _ "From there, download and install Ghostscript.") Case $_ERROR_imagemagickNotInstalled MsgBox(0,"Error", "ImageMagick is missing." & @CRLF & @CRLF & _ "Please go to:" & @CRLF & _ "http://www.imagemagick.org/script/binary-releases.php#windows" & @CRLF & @CRLF & _ "From there, download the Q16-windows-dll version." & @CRLF & @CRLF & _ "Make sure the installation option " & $QQ & "Install ImageMagickObject" & @CRLF & _ "OLE Control for VBscript, Visual Basic, and WSH." & $QQ & " is enabled.") Case $_ERROR_MSVisualCPlusPlus2008RedistNotInstalled MsgBox(0,"Error", "Microsoft Visual C++ 2008 SP1 is not available." & @CRLF & @CRLF & _ "Please go to:" & @CRLF & _ "http://www.microsoft.com/downloads/details.aspx?familyid=A5C84275-3B97-4AB7-A40D-3802B2AF5FC2" & @CRLF & @CRLF & _ "From there, download and install vcredist_x86.") Case $_ERROR_PDF_FileInconsistent MsgBox(0,"Error", "The current processed pdf file contains some errors." & @CRLF & @CRLF & _ "To prevent further bad results, you could" & @CRLF & _ "try to open the file with a pdf viewer and" & @CRLF & _ "print from there to a pdf file to get a" & @CRLF & _ "new and more consistent pdf file.") Case Else MsgBox(0,"Error", String($_ERROR_Number)) EndSwitch Exit EndFunc