Command.File.PDFtk: A wrapper for PDFtk as an internal command

This command offers @tbone 's great PDFToolkit Menu functionality as an internal command so it is easily accessible from everywhere (eg a context menu for a pdf file type group).

As the original menu it expects the /pdftoolkit folder alias pointing to the PDF toolkit root path (the one containing the /bin folder).

Parameters
The following commands are available (but just one at a time). For the sake of simplicity I did not (yet?) add further parameters thus the values for filenames, page ragens... are entered via dialogs (just like in the original menu buttons code).

  • PDFTK APPENDFROMDEST/S
  • PDFTK EXTRACT/K[all,even,odd,range]
  • PDFTK MERGE/S
  • PDFTK MERGEODDEVEN/S
  • PDFTK ROTATE/K[left,right,reverse,llrr,rrll]
  • PDFTK SPLIT/S
  • PDFTK PROTECT/S
  • PDFTK UNPROTECT/S
  • PDFTK COMPRESS/S

Installation / Downloads
How to install https://resource.dopus.com/t/how-to-use-buttons-and-scripts-from-this-forum/3546/3.

// Command.File.PDFtk
// (c) 2022 Felix

// Based on tbone's great PDF Toolkit Menu
// https://resource.dopus.com/t/pdf-toolkit-menu-pdf-merge-extract-protect-unprotect-organize/22348

// This is a script for Directory Opus.
// See https://www.gpsoft.com.au/DScripts/redirect.asp?page=scripts for development information.



// Called by Directory Opus to initialize the script
function OnInit(initData)
{
	initData.name = "Command.File.PDFtk";
	initData.version = "1.0 (2022.03.11)";
	initData.copyright = "(c) 2022 Felix";
	initData.url = "https://resource.dopus.com/t/command-file-pdftk-a-wrapper-for-pdftk-as-an-internal-command/40771";
	initData.desc = "Wrapper for PDF Toolkit as internal command";
	initData.default_enable = true;
	initData.min_version = "12.0";
	initData.group = "Column";

	var cmd = initData.AddCommand();
	cmd.name = "PDFTK";
	cmd.method = "OnPDFTK";
	cmd.desc = "Wrapper for PDF Toolkit as internal command";
	cmd.label = "PDFTK";
	cmd.template = "APPENDFROMDEST/S,EXTRACT/K[all,even,odd,range],MERGE/S,MERGEODDEVEN/S,ROTATE/K[left,right,reverse,llrr,rrll],SPLIT/S,PROTECT/S,UNPROTECT/S,COMPRESS/S";
	cmd.hide = false;
	cmd.icon = "rename2";
}

var PDFtkBinaryPath = null;


// Implement the PDFTK command
function OnPDFTK(scriptCmdData)
{
	PDFtkBinaryPath = "\"" + DOpus.FSUtil.Resolve('/pdftoolkit\\bin\\pdftk.exe') + "\"";
	if(!PDFtkBinaryPath)
	{
		Log("Pdftk binary path not found", true);
		return;
	}
	
	var args = scriptCmdData.func.args;
	if(args.got_arg.MERGE)
		Merge(scriptCmdData);
	else if(args.got_arg.SPLIT)
		Split(scriptCmdData);
	else if(args.got_arg.ROTATE)
		Rotate(scriptCmdData, args.ROTATE.toLowerCase());
	else if(args.got_arg.EXTRACT)
		Extract(scriptCmdData, args.EXTRACT.toLowerCase());
	else if(args.got_arg.PROTECT)
		Protect(scriptCmdData);
	else if(args.got_arg.UNPROTECT)
		Unprotect(scriptCmdData);
	else if(args.got_arg.COMPRESS)
		Compress(scriptCmdData);
	else if(args.got_arg.APPENDFROMDEST)
		AppendFromDest(scriptCmdData);
}

function Merge(data)
{
	var dlg = DOpus.Dlg;
	var tab = data.func.sourcetab;
	var selFiles = tab.selected_files;
	var cmd = data.func.command;
	cmd.deselect = false; cmd.SetModifier('runmode', 'hide'); 
	var shell = new ActiveXObject("WScript.Shell");
	if (selFiles.count<2) 
		return;
	
	var input = dlg.GetString("Enter new *basename* for merged PDF file (will be overwritten if exists):", selFiles(0).name_stem+"_merged", 255,
	"Ok|Cancel", "Merge PDF", data.func.sourcetab.lister);
	if (!input)
		return;

	var path = (selFiles(0).path+"").replace('(\/|\\\\)$', "");
	//var pdftkPath = DOpus.FSUtil.Resolve('/pdftoolkit\\bin\\pdftk.exe');
	var pdfFilePaths = PrepItems(selFiles, "realpath").join(" ");
	cmd.RunCommand('"'+PDFtkBinaryPath+'" '+pdfFilePaths+' cat output "'+path+'\\'+input+'.pdf"');

	var mergeTextFiles = true;
	var textFilePaths = PrepItems(selFiles, "realpath", "", "", false);
	
	for(var i=0;i<textFilePaths.length;i++)
	{
		textFilePaths[i] = textFilePaths[i].replace(new RegExp("\.pdf$","i"),".txt");
		if (!DOpus.FSUtil.Exists(textFilePaths[i])) 
		{ 
			mergeTextFiles = false;
			break;
		}
		textFilePaths[i] = "'"+textFilePaths[i]+"'";
		Log(textFilePaths[i]);
	}

	var cmdLine = "powershell.exe -noexit -executionpolicy bypass -command \"&{$f = @("+textFilePaths.join(",")+"); $dest='"+path+"\\"+input+".txt'; $v='';"+
		"for($i=0;$i -lt $f.count;$i++) {$v+=gc -lit $f[$i] -en utf8 -raw;$v+=[char]13+[char]10;} sc -lit $dest -val $v -enc utf8;}\"";
	if (mergeTextFiles) shell.Run(cmdLine, 0, true);
	else DOpus.Output("Skipping merge of txt sidecar files (no full set found).");
}

function MergeOddEven(data)
{
	var cmd = data.func.command, dlg = DOpus.Dlg, tab = data.func.sourcetab, selFiles = tab.selected_files;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	var input = dlg.GetString(	"Enter name of new odd/even merged file:", selFiles(0).name_stem+'_oddeven.pdf', 255,
								"Ok|Cancel", "Merge odd/even", data.func.sourcetab.lister);
	
	var filePath1 = selFiles(0).realpath;
	var baseName1 = selFiles(0).name_stem;
	var filePath2 = selFiles(1).realpath;
	var baseName2 = selFiles(1).name_stem;
	var path = (selFiles(0).path+"").replace('(\/|\\\\)$', '');
	//var pdftkPath = DOpus.FSUtil.Resolve('/pdftoolkit\\bin\\pdftk.exe');
	cmd.RunCommand('"'+PDFtkBinaryPath+'" A="'+filePath1+'" B="'+filePath2+'" shuffle A B output "'+path+'\\'+input+'"');
}

function Split(data){
	var cmd = data.func.command, dlg = DOpus.Dlg, tab = data.func.sourcetab, selFiles = tab.selected_files;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	var input = dlg.GetString(	"Enter number of first page to be separated:", "2", 3,
								"Ok|Cancel", "Split PDF", data.func.sourcetab.lister);
	if (!input || input*1<2) return;
	var filePath = selFiles(0).realpath;
	var baseName = selFiles(0).name_stem;
	var path = (selFiles(0).path+"").replace('(\/|\\\\)$', '');
	//var pdftkPath = DOpus.FSUtil.Resolve('/pdftoolkit\\bin\\pdftk.exe');
	cmd.RunCommand('"'+PDFtkBinaryPath+'" "'+filePath+'" cat 1-'+(input-1)+' output "'+path+'\\'+baseName+'_1-'+(input-1)+'.pdf"');
	cmd.RunCommand('"'+PDFtkBinaryPath+'" "'+filePath+'" cat '+(input)+'-end output "'+path+'\\'+baseName+'_'+(input)+'-end.pdf"');
}

function Rotate(data, param)
{
	var cmd = data.func.command;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	cmd.SetType("msdos");

	if(param == "left")
		cmd.RunCommand(PDFtkBinaryPath + " {file$} rotate 1-endleft output {file|noext}_(-90°).pdf");
	else if(param == "right")
		cmd.RunCommand(PDFtkBinaryPath + " {file$} rotate 1-endright output {file|noext}_(+90°).pdf");
	else if(param == "reverse")
		cmd.RunCommand(PDFtkBinaryPath + " {filepath$} cat 1-enddown output {filepath|noext}_(+180°).pdf");
	else if(param == "llrr")
		RotateScanned(data, "left");
	else if(param == "rrll")
		RotateScanned(data, "right");
}

function RotateScanned(data, rotDirection) 
{
	var cmd = data.func.command, dlg = DOpus.Dlg, tab = data.func.sourcetab, selFiles = tab.selected_files;
	cmd.deselect = false; cmd.SetModifier('runmode', 'hide'); var shell = new ActiveXObject("WScript.Shell");
	if (selFiles.count<2 || selFiles.count%2 != 0){
		Log("Bad number of selected files, need even number and more than 1.", true);
		return;
	}
	
	var input = dlg.GetString("Enter new *basename* for renamed PDF files (will be overwritten if exists):", "book_pages", 255,
	"Ok|Cancel", "Rename PDFs", data.func.sourcetab.lister);
	if (!input) return;

	var path = (selFiles(0).path+"").replace('(\/|\\\\)$', "");
	//var pdftkPath = DOpus.FSUtil.Resolve('/pdftoolkit\\bin\\pdftk.exe');

	var pageCount = selFiles.count;
	var indexOdd = pageCount;
	var indexEven = 1;
	//var rotDirection = "left";
	for(var i=0;i<pageCount;i+=2)
	{
		RunWS('"'+PDFtkBinaryPath+'" "'+selFiles(i).realpath+'" rotate 1-end'+rotDirection+' output "'+selFiles(i).realpath+'.tmp.pdf"');
		RunDO('Rename "'+selFiles(i).name+'.tmp.pdf" TO="'+input+Pad(indexOdd,"0")+'.pdf"'); indexOdd--;

		RunWS('"'+PDFtkBinaryPath+'" "'+selFiles(i+1).realpath+'" rotate 1-end'+rotDirection+' output "'+selFiles(i+1).realpath+'.tmp.pdf"');
		RunDO('Rename "'+selFiles(i+1).name+'.tmp.pdf" TO="'+input+Pad(indexEven,"0")+'.pdf"'); indexEven++;

		rotDirection = rotDirection == "right" ? "left" : "right";
	}
}

function Extract(data, param)
{
	var cmd = data.func.command;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	cmd.SetType("msdos");
	cmd.AddLine("@firstfileonly");
	
	if(param == "all")
	{
		cmd.AddLine(PDFtkBinaryPath + " {file} burst output {file|noext}_%%03d.pdf");
		cmd.Run();
	}
	else if(param == "even")
	{
		cmd.AddLine("@set fileMod={dlgstringS|Enter name for PDF containing even pages only:\n(will be overwritten if exists)|{file$|noext}_even.pdf}");
		cmd.AddLine(PDFtkBinaryPath + " {file$} cat 1-endeven output \"{$fileMod}\" dont_ask");
		cmd.Run();
	}
	else if(param == "odd")
	{
		cmd.AddLine("@set fileMod={dlgstringS|Enter name for PDF containing odd pages only:\n(will be overwritten if exists)|{file$|noext}_odd.pdf}");
		cmd.AddLine(PDFtkBinaryPath + " {file$} cat 1-endodd output \"{$fileMod}\" dont_ask");
		cmd.Run();
	}
	else if(param == "range")
	{
		cmd.AddLine("@set fileMod={dlgstringS|Enter name for new modified PDF file:\n(will be overwritten if exists)|{file$|noext}_modified.pdf}");
		cmd.AddLine("@set pages={dlgstringS|Enter pages to keep (example: 1 6-8 2-3 10-end):}");
		cmd.AddLine(PDFtkBinaryPath + " {file$} cat {$pages} output \"{$fileMod}\" dont_ask");
		cmd.Run();
	}
}

function Protect(data)
{
	var cmd = data.func.command;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	cmd.SetType("msdos");
	cmd.AddLine("@set pass={dlgstring|Enter new password to protect PDF file:\n(will create \"{file$|noext}_protected.pdf\")}");
	cmd.AddLine(PDFtkBinaryPath + " {file} output {file|noext}_protected.pdf user_pw \"{$pass}\" dont_ask");
	cmd.Run();
}

function Unprotect(data)
{
	var cmd = data.func.command;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	cmd.SetType("msdos");
	cmd.AddLine("@set pass={dlgstring|Enter password to unprotect PDF file:\n(will create \"{file$|noext}_unprotected.pdf\")}");
	cmd.AddLine(PDFtkBinaryPath + " {file} input_pw \"{$pass}\" output {file|noext}_unprotected.pdf dont_ask");
	cmd.Run();
}

function Compress(data)
{
	var cmd = data.func.command;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	cmd.SetType("msdos");
	cmd.AddLine("@set fileComp={dlgstringS|Enter *basename* for compressed PDF file (will be overwritten if exists)|{file$|noext}_compressed}");
	cmd.AddLine(PDFtkBinaryPath + " {filepath$} output \"{$fileComp}.pdf\" compress");
	cmd.Run();
}

function AppendFromDest(data)
{
	var cmd = data.func.command;
	cmd.SetModifier('runmode','hide');
 	cmd.deselect = false;
	cmd.SetType("msdos");
	cmd.AddLine("@set fileAppended={dlgstringS|Enter name for appended PDF file (will be overwritten if exists)|{file$|noext}_appended.pdf}");
	cmd.AddLine(PDFtkBinaryPath + " {filepath$} {allfilepathdest$} cat output \"{$fileAppended}\" dont_ask ");
	cmd.Run();
}

function PrepItems(items, property, prefix, suffix, addQuotes)
{
	function PrepParam(p, defValue){ return typeof p==='undefined'?defValue:p;}
	property = PrepParam(property,"name"); prefix = PrepParam(prefix,"");
	suffix = PrepParam(suffix,""); addQuotes = PrepParam(addQuotes,true);
	for(var i=0,names=[];i<items.count;i++)
		names[names.length] = (addQuotes?'"':'')+prefix+items(i)[property]+suffix+(addQuotes?'"':'');
	return names;
}

function Pad(str, character, digits)
{
	character = character || ' ';
	digits = digits || 4;
	str = str+'';
	while(str.length<digits) str=character+str;
	return str;
}

function RunDO(cmdLine)
{
	Log(cmdLine);
	cmd.RunCommand(cmdLine);
}

function RunWS(cmdLine,wait)
{
	wait = typeof wait == "undefined" ? true : wait;
	Log(cmdLine);
	var shell = new ActiveXObject("WScript.Shell");
	shell.Run(cmdLine,0,wait);
}

function Log(msg, e)
{
	DOpus.Output(String(msg), e || false);
}
1 Like