The Unit below is an attempt to convert the pdfp command line program for use in Delphi. It seems to work, but I am not real fluent in C so I'm sure there will be some issues to be found with it.
I would welcome any suggestions on improving it.
There is only one function to call, (Printpdf). Pass in the filename, printer name and the three other parameters as described in the pdfp program
-o orientation : set orientation 1=portrait, 2=landscape
-d duplex : set duplex mode (if supported by printer) 1=none, 2=long side, 3=short side
-c copies : set number of copies to print copies=1 to 99
I put in the code to look for Adobe 8 so it should work with it, but I have not tested at all with version 8, nor did I put in the additional commands that are in pdfp8.
----------------------------------------------------------------------------------------
unit pdfdde;
// Delphi conversion of pdfp command line program from // http://www.esnips.com/web/PDFTools
interface
// FileName must contain complete path function PrintPdf(Filename,PrinterName : string;Orientation,duplex,copies : integer): boolean;
implementation uses windows,ShellAPI,sysutils,messages,ddeman,winspool,printers;
var hWndAdobe : HWND;
function SetPrinterProperties(pPrinterName : pChar;pDevModeNew : PDEVMODE) : boolean; var hPrinter : THandle; dwNeeded : DWORD; pi2 : PPrinterInfo2; pDevModeVar : PDEVMODE; pDevModeVar2 : PDEVMODE; pd : PRINTER_DEFAULTS; bFlag : boolean; lFlag : integer; begin // Open printer handle ZeroMemory(@pd, sizeof(pd)); pd.DesiredAccess := PRINTER_ACCESS_USE; //PRINTER_ALL_ACCESS; bFlag := OpenPrinter(pPrinterName, hPrinter, @pd); if (bFlag = false) or (hPrinter = 0) then begin result := FALSE; exit; end;
// The first GetPrinter tells you how big the buffer should be in // order to hold all of PRINTER_INFO_2. Note that this should fail with // ERROR_INSUFFICIENT_BUFFER. If GetPrinter fails for any other reason // or dwNeeded isn't set for some reason, then there is a problem... SetLastError(0); bFlag := GetPrinter(hPrinter, 2, nil, 0, @dwNeeded); if ((bFlag) or (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) or (dwNeeded = 0)) then begin ClosePrinter(hPrinter); result := FALSE; exit; end;
// Allocate enough space for PRINTER_INFO_2... GetMem(pi2, dwNeeded); if (pi2 = nil) then begin ClosePrinter(hPrinter); result := FALSE; exit; end;
// The second GetPrinter fills in all the current settings, so all you // need to do is modify what you're interested in... bFlag := GetPrinter(hPrinter, 2, pi2, dwNeeded, @dwNeeded); if (bFlag = false) then begin freemem(pi2); ClosePrinter(hPrinter); result := FALSE; exit; end;
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling // DocumentProperties... if (pi2.pDevMode = nil) then begin dwNeeded := DocumentProperties(0, hPrinter, //* Handle to our printer. */ pPrinterName, //* Name of the printer. */ pDevModevar^, //* Asking for size, so */ pDevModeVar2^, //* these are not used. */ 0); //* Zero returns buffer size. */
if (dwNeeded <= 0) then begin FreeMem(pi2); ClosePrinter(hPrinter); result := FALSE; end;
GetMem(pDevModeVar,dwneeded);
lFlag := DocumentProperties(0, hPrinter, pPrinterName, pDevModeVar^, pDevModeVar2^, DM_OUT_BUFFER);
if (lFlag <> IDOK) or (pDevModeVar = nil) then begin FreeMem(pDevModeVar); FreeMem(pi2); ClosePrinter(hPrinter); result := FALSE; end;
pi2.pDevMode := pDevModeVar; end;
if (pDevModeNew.dmFields and DM_COPIES) <> 0 then if(pi2.pDevMode.dmCopies <> pDevModeNew.dmCopies) then begin // Specify exactly what we are attempting to change... pi2.pDevMode.dmFields := pi2.pDevMode.dmFields or DM_COPIES; pi2.pDevMode.dmCopies := pDevModeNew.dmCopies; end;
if (pDevModeNew.dmFields and DM_ORIENTATION <> 0) then if(pi2.pDevMode.dmOrientation <> pDevModeNew.dmOrientation) then begin // Specify exactly what we are attempting to change... pi2.pDevMode.dmFields := pi2.pDevMode.dmFields or DM_ORIENTATION; pi2.pDevMode.dmOrientation := pDevModeNew.dmOrientation; end;
if (pDevModeNew.dmFields and DM_DUPLEX) <> 0 then if(pi2.pDevMode.dmDuplex <> pDevModeNew.dmDuplex) then begin // Specify exactly what we are attempting to change... pi2.pDevMode.dmFields := pi2.pDevMode.dmFields or DM_DUPLEX; pi2.pDevMode.dmDuplex := pDevModeNew.dmDuplex; end;
if (pDevModeNew.dmFields and DM_SCALE) <> 0 then if(pi2.pDevMode.dmScale <> pDevModeNew.dmScale) then begin // Specify exactly what we are attempting to change... pi2.pDevMode.dmFields := pi2.pDevMode.dmFields or DM_SCALE; //pi2.pDevMode.dmNup = DMNUP_SYSTEM; pi2.pDevMode.dmDisplayFlags := 1; pi2.pDevMode.dmScale := pDevModeNew.dmScale; end;
// Do not attempt to set security descriptor... pi2.pSecurityDescriptor := Nil;
// Make sure the driver-dependent part of devmode is updated... lFlag := DocumentProperties(0, hPrinter, pPrinterName, pi2.pDevMode^, pi2.pDevMode^, DM_IN_BUFFER or DM_OUT_BUFFER);
if (lFlag <> IDOK) then begin FreeMem(pi2); ClosePrinter(hPrinter); result := FALSE; exit; end;
// Update printer information... bFlag := WinSpool.SetPrinter(hPrinter, 2, pi2, 0);
// Note that this is triggered on network printers, although the values // get set, so we will ignore this for now //* if (!bFlag) // // The driver doesn't support, or it is unable to make the change... // { // GlobalFree(pi2); // ClosePrinter(hPrinter); // if (pDevMode) // GlobalFree(pDevMode); // return FALSE; // } //*/ // Tell other apps that there was a change... SendMessageTimeout(HWND_BROADCAST, WM_DEVMODECHANGE, 0, LPARAM(PChar(pPrinterName)), SMTO_NORMAL, 1000, PDWORD(nil)^);
// Clean up... if (pi2 <> nil) then freemem(pi2); if (hPrinter <> 0) then ClosePrinter(hPrinter);
result := TRUE; end;
function GetPrinterDevMode(pDevice : PChar) : PDEVMODE;
var hPrinter : THandle; pDevModeVar : PDEVMODE; pDevModeVar2 : PDEVMODE; dwNeeded : DWORD; dwRet : DWORD; begin //* Start by opening the printer */ if not OpenPrinter(pDevice, hPrinter, nil) then begin result := nil; end;
// * // * Step 1: // * Allocate a buffer of the correct size. // * dwNeeded := DocumentProperties(0, hPrinter, //* Handle to our printer. */ pDevice, //* Name of the printer. */ pDevModevar^, //* Asking for size, so */ pDevModeVar^, //* these are not used. */ 0); //* Zero returns buffer size. */
GetMem(pDevModeVar, dwNeeded);
// * // * Step 2: // * Get the default DevMode for the printer // * dwRet := DocumentProperties(0, hPrinter, pDevice, pDevModeVar^, //* The address of the buffer to fill. */ pDevModeVar2^, //* Not using the input buffer. */ DM_OUT_BUFFER); //* Have the output buffer filled. */ if (dwRet <> IDOK) then begin //* If failure, cleanup and return failure. */ freemem(pDevModeVar); ClosePrinter(hPrinter); result := nil; end;
//* Finished with the printer */ ClosePrinter(hPrinter);
//* Return the DevMode structure. */ result := pDevModeVar; end;
function GetExeName(FileName,FilePath : string; var PdfExeName,Msg : string; var RetCode : integer): Boolean; var FileNameArray : array [0..MAX_PATH -1] of char; DefaultPathArray : array [0..MAX_PATH -1] of char; ResultArray : array [0..MAX_PATH -1] of char;
begin strpcopy(FileNameArray,FileName); strpcopy(DefaultPathArray,FilePath); RetCode := FindExecutable(FileNameArray,DefaultPathArray,ResultArray); PdfExeName := ResultArray;
Result := false; case RetCode of SE_ERR_NOASSOC : msg := 'File Association Not Found'; SE_ERR_FNF : msg := 'File Not Found'; SE_ERR_PNF : msg := 'path not found '; SE_ERR_ACCESSDENIED : msg := 'access denied'; SE_ERR_OOM : msg := 'out of memory '; SE_ERR_DLLNOTFOUND : msg := 'dll not found'; SE_ERR_SHARE : msg := 'sharing error'; SE_ERR_ASSOCINCOMPLETE : msg := 'association incomplete'; SE_ERR_DDETIMEOUT : msg := 'DDE Timeout'; SE_ERR_DDEFAIL : msg := 'DDE Failure'; SE_ERR_DDEBUSY : msg := 'DDE Busy'; else begin //I guess it worked Result := true; msg := 'Retcode ' + inttostr(RetCode); end; end; end;
procedure HideAdobe; begin ShowWindow (hWndAdobe, SW_RESTORE); MoveWindow (hWndAdobe, -100, -100, 0, 0, TRUE); ShowWindow (hWndAdobe, SW_RESTORE); ShowWindow (hWndAdobe, SW_HIDE); end;
function IsAdobeRunning : boolean; begin
hWndAdobe := FindWindow('AdobeAcrobat', nil);
if hWndAdobe > 0 then Result := true else begin hWndAdobe := FindWindow('AcrobatSDIWindow', nil); if hWndAdobe > 0 then Result := true else Result := false; end; end;
function PrintPdf(Filename,PrinterName : string;Orientation,duplex,copies : integer): boolean; var PrinterDevice : array[0..MAX_PATH -1] of char; PrinterDriver : array[0..MAX_PATH -1] of char; PrinterPort : array[0..MAX_PATH -1] of char; CommandBuff : array[0..2047] of char; FilePathName : array[0..MAX_PATH -1] of char; FileNameArray : array [0..MAX_PATH -1] of char; DefaultPathArray : array [0..MAX_PATH -1] of char; CommandStr : string; hPrinter: THandle; DeviceMode: THandle; dwNeeded : DWORD; ppi2 : PPrinterInfo2; Foundit : boolean; RetCode : integer; msg : string; FileNameStr : string; FilePathStr : string; DdeConv : TDdeClientConv; WasRunning : boolean; pDevMode1 : PDeviceMode; pDevMode2 : PDeviceMode;
begin
if (copies < 1) or (copies > 10) then copies := 1;
result := false; FileNameStr := extractfilename(Filename); FilePathStr := extractfilepath(Filename); strpcopy(FileNameArray,FileNameStr); strpcopy(DefaultPathArray,FilePathStr); strpcopy(FilePathName,Filename);
Foundit := GetExeName(Filename,FilePathStr,FileNameStr,msg,RetCode); if not Foundit then begin //log errors here exit; //bail out - result = false end;
// Make sure printer name is valid strPcopy(PrinterDevice,PrinterName); if not OpenPrinter(PrinterDevice, hPrinter, nil) then begin // If not valid, log the problem & get default printer printer.GetPrinter(PrinterDevice,PrinterDriver,PrinterPort,DeviceMode); //log errors here if not OpenPrinter(PrinterDevice, hPrinter, nil) then begin //log errors here exit; //bail out - result = false end; end; // Find out how much space is needed for printer info GetPrinter(hPrinter, 2, nil, 0, @dwNeeded); if (dwNeeded = 0) then begin //log errors here ClosePrinter(hPrinter); exit; //bail out - result = false end;
//// Allocate enough space for PRINTER_INFO_2... GetMem(ppi2, dwNeeded); // The second GetPrinter() will fill in all the current information try if GetPrinter(hPrinter, 2, ppi2, dwNeeded, @dwNeeded)then begin strcopy(PrinterPort,ppi2.pPortName); strcopy(PrinterDriver,ppi2.pDriverName); end; finally freemem(ppi2); ClosePrinter(hPrinter); end;
// Get default printer settings pDevMode1 := GetPrinterDevMode(PrinterDevice); pDevMode2 := GetPrinterDevMode(PrinterDevice);
pDevMode2.dmDuplex := duplex; pDevMode2.dmFields := pDevMode2.dmFields or DM_DUPLEX; pDevMode1.dmFields := pDevMode1.dmFields or DM_DUPLEX;
if orientation = 2 then pDevMode2.dmOrientation := DMORIENT_LANDSCAPE else pDevMode2.dmOrientation := DMORIENT_PORTRAIT; pDevMode2.dmFields := pDevMode2.dmFields or DM_ORIENTATION; pDevMode1.dmFields := pDevMode1.dmFields or DM_ORIENTATION;
pDevMode2.dmCopies := copies; pDevMode2.dmFields := pDevMode2.dmFields or DM_COPIES; pDevMode1.dmFields := pDevMode1.dmFields or DM_COPIES;
SetPrinterProperties(PrinterDevice,pDevMode2);
WasRunning := true;
if not IsAdobeRunning then begin WasRunning:=FALSE;
ShellExecute(0,'open',FileNameArray,nil,nil,SW_HIDE);
Sleep(3000);
HideAdobe();
if not IsAdobeRunning then Sleep(3000);
HideAdobe();
end;
DdeConv := TDdeClientConv.Create(nil); try
DdeConv.SetLink('acroview','control'); DdeConv.OpenLink;
//CommandStr := '[FilePrintSilent("' + FilePathName + '")]'; CommandStr := '[FilePrintTo("' + FilePathName + '","' + PrinterDevice + '","' + PrinterDriver + '","' + PrinterPort +'")]';
strpcopy(CommandBuff,CommandStr);
DdeConv.ExecuteMacro(CommandBuff,false); //Log error here DdeConv.CloseLink; sleep(1000); finally DdeConv.Free; DdeConv := nil; end;
//TODO? make sure we are finished spooling first
if not WasRunning then begin if IsAdobeRunning then begin Sleep(1000); // Ask Acrobat/Reader to close PostMessage (hWndAdobe, WM_CLOSE, 0, 0); end; end;
SetPrinterProperties(PrinterDevice,pDevMode1);
result := true; end;
end.
|