User Tools

Site Tools


software:pdfprocessing:makepdf

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:

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
1)
Alternatively, when not found, click here: vcredist_x86.exe
2)
not fully tested
software/pdfprocessing/makepdf.txt · Last modified: 2012/05/31 05:55 by admin