{ $Project$ $Workfile$ $Revision$ $DateUTC$ $Id$ This file is part of the Indy (Internet Direct) project, and is offered under the dual-licensing agreement described on the Indy website. (http://www.indyproject.org/) Copyright: (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. } { $Log$ } { Rev 1.12 7/24/04 12:56:14 PM RLebeau Compiler fix for Print(TIdBytes) Rev 1.11 7/23/04 7:15:16 PM RLebeau Added extra exception handling to various Print...() methods Rev 1.10 2004.05.20 11:36:50 AM czhower IdStreamVCL Rev 1.9 2004.03.03 11:54:32 AM czhower IdStream change Rev 1.8 2004.02.03 5:43:56 PM czhower Name changes Rev 1.7 1/21/2004 3:11:22 PM JPMugaas InitComponent Rev 1.6 10/24/2003 02:54:52 PM JPMugaas These should now work with the new code. Rev 1.5 2003.10.24 10:43:10 AM czhower TIdSTream to dos Rev 1.4 2003.10.12 4:04:00 PM czhower compile todos Rev 1.3 2/24/2003 09:07:26 PM JPMugaas Rev 1.2 2/6/2003 03:18:08 AM JPMugaas Updated components that compile with Indy 10. Rev 1.1 12/6/2002 05:30:18 PM JPMugaas Now decend from TIdTCPClientCustom instead of TIdTCPClient. Rev 1.0 11/13/2002 07:56:22 AM JPMugaas 27.07. rewrite component for integration in Indy core library } unit IdLPR; { Indy Line Print Remote TIdLPR Version 9.1.0 Original author Mario Mueller home: www.hemasoft.de mail: babelfisch@daybyday.de } interface {$i IdCompilerDefines.inc} uses Classes, IdAssignedNumbers, IdGlobal, IdException, IdTCPClient, IdComponent, IdBaseComponent; type TIdLPRFileFormat = (ffCIF, // CalTech Intermediate Form ffDVI, // DVI (TeX output). ffFormattedText, //add formatting as needed to text file ffPlot, // Berkeley Unix plot library ffControlCharText, //text file with control charactors ffDitroff, // ditroff output ffPostScript, //Postscript output file ffPR,//'pr' format {Do not Localize} ffFORTRAM, // FORTRAN carriage control ffTroff, //Troff output ffSunRaster); // Sun raster format file const DEF_FILEFORMAT = ffControlCharText; DEF_INDENTCOUNT = 0; DEF_BANNERPAGE = False; DEF_OUTPUTWIDTH = 0; DEF_MAILWHENPRINTED = False; type TIdLPRControlFile = class(TPersistent) protected FBannerClass: String; // 'C' {Do not Localize} FHostName: String; // 'H' {Do not Localize} FIndentCount: Integer; // 'I' {Do not Localize} FJobName: String; // 'J' {Do not Localize} FBannerPage: Boolean; // 'L' {Do not Localize} FUserName: String; // 'P' {Do not Localize} FOutputWidth: Integer; // 'W' {Do not Localize} FFileFormat : TIdLPRFileFormat; FTroffRomanFont : String; //substitue the Roman font with the font in file FTroffItalicFont : String;//substitue the Italic font with the font in file FTroffBoldFont : String; //substitue the bold font with the font in file FTroffSpecialFont : String; //substitue the special font with the font //in this file FMailWhenPrinted : Boolean; //mail me when you have printed the job public constructor Create; procedure Assign(Source: TPersistent); override; property HostName: String read FHostName write FHostName; published property BannerClass: String read FBannerClass write FBannerClass; property IndentCount: Integer read FIndentCount write FIndentCount default DEF_INDENTCOUNT; property JobName: String read FJobName write FJobName; property BannerPage: Boolean read FBannerPage write FBannerPage default DEF_BANNERPAGE; property UserName: String read FUserName write FUserName; property OutputWidth: Integer read FOutputWidth write FOutputWidth default DEF_OUTPUTWIDTH; property FileFormat: TIdLPRFileFormat read FFileFormat write FFileFormat default DEF_FILEFORMAT; {font data } property TroffRomanFont : String read FTroffRomanFont write FTroffRomanFont; property TroffItalicFont : String read FTroffItalicFont write FTroffItalicFont; property TroffBoldFont : String read FTroffBoldFont write FTroffBoldFont; property TroffSpecialFont : String read FTroffSpecialFont write FTroffSpecialFont; {misc} property MailWhenPrinted : Boolean read FMailWhenPrinted write FMailWhenPrinted default DEF_MAILWHENPRINTED; end; type TIdLPRStatus = (psPrinting, psJobCompleted, psError, psGettingQueueState, psGotQueueState, psDeletingJobs, psJobsDeleted, psPrintingWaitingJobs, psPrintedWaitingJobs); type TIdLPRStatusEvent = procedure(ASender: TObject; const AStatus: TIdLPRStatus; const AStatusText: String) of object; type TIdLPR = class(TIdTCPClientCustom) protected FOnLPRStatus: TIdLPRStatusEvent; FQueue: String; FJobId: Integer; FControlFile: TIdLPRControlFile; procedure DoOnLPRStatus(const AStatus: TIdLPRStatus; const AStatusText: String); procedure SeTIdLPRControlFile(const Value: TIdLPRControlFile); procedure CheckReply; function GetJobId: String; procedure SetJobId(const Value: String); procedure InternalPrint(Data: TStream); function GetControlData: String; procedure InitComponent; override; public {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} constructor Create(AOwner: TComponent); reintroduce; overload; {$ENDIF} destructor Destroy; override; procedure Connect; override; procedure Print(const AText: String); overload; procedure Print(const ABuffer: TIdBytes); overload; procedure PrintFile(const AFileName: String); function GetQueueState(const AShortFormat: Boolean = False; const AList : String = '') : String; {Do not Localize} procedure PrintWaitingJobs; procedure RemoveJobList(const AList: String; const AAsRoot: Boolean = False); property JobId: String read GetJobId write SetJobId; published property Queue: String read FQueue write FQueue; property ControlFile: TIdLPRControlFile read FControlFile write SeTIdLPRControlFile; property Host; property Port default IdPORT_LPD; property OnLPRStatus: TIdLPRStatusEvent read FOnLPRStatus write FOnLPRStatus; end; type EIdLPRErrorException = class(EIdException); implementation uses {$IFDEF DOTNET} IdStreamNET, {$ELSE} IdStreamVCL, {$ENDIF} IdGlobalProtocols, IdResourceStringsProtocols, IdStack, IdStackConsts, SysUtils; { TIdLPR } {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} constructor TIdLPR.Create(AOwner: TComponent); begin inherited Create(AOwner); end; {$ENDIF} procedure TIdLPR.InitComponent; begin inherited InitComponent; Port := IdPORT_LPD; Queue := 'pr1'; {Do not Localize} FJobId := 1; FControlFile := TIdLPRControlFile.Create; // Restriction in RFC 1179 // The source port must be in the range 721 to 731, inclusive. BoundPortMin := 721; BoundPortMax := 731; end; procedure TIdLPR.Connect; var LPort: TIdPort; begin // RLebeau 3/7/2010: there is a problem on Windows where sometimes it will // not raise a WSAEADDRINUSE error in TIdSocketHandle.TryBind(), but will // delay it until TIdSocketHandle.Connect() instead. So we will loop here // to force a Connect() on each port, rather than let TIdSocketHandle do // the looping in BindPortReserved(). If this logic proves useful in other // protocols, we can move it into TIdSocketHandle later on... // AWinkelsdorf 3/9/2010: Implemented, adjusted to use BoundPortMax and // BoundPortMin // looping backwards because that is what TIdSocketHandle.BindPortReserved() does for LPort := BoundPortMax downto BoundPortMin do begin BoundPort := LPort; try inherited Connect; Exit; except on E: EIdCouldNotBindSocket do begin end; on E: EIdSocketError do begin if E.LastError <> Id_WSAEADDRINUSE then begin raise; end; // Socket already in use, cleanup and try again with the next Disconnect; end; end; end; // no local ports could be bound successfully raise EIdCanNotBindPortInRange.CreateFmt(RSCannotBindRange, [BoundPortMin, BoundPortMax]); end; procedure TIdLPR.Print(const AText: String); var LStream: TStream; LEncoding: IIdTextEncoding; begin LStream := TMemoryStream.Create; try LEncoding := IndyTextEncoding_8Bit; WriteStringToStream(LStream, AText, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); LEncoding := nil; LStream.Position := 0; InternalPrint(LStream); finally FreeAndNil(LStream); end; end; procedure TIdLPR.Print(const ABuffer: TIdBytes); var LStream: TMemoryStream; begin LStream := TMemoryStream.Create; try WriteTIdBytesToStream(LStream, ABuffer); LStream.Position := 0; InternalPrint(LStream); finally FreeAndNil(LStream); end; end; procedure TIdLPR.PrintFile(const AFileName: String); var LStream: TIdReadFileExclusiveStream; p: Integer; begin p := RPos(GPathDelim, AFileName); ControlFile.JobName := Copy(AFileName, p+1, Length(AFileName)-p); LStream := TIdReadFileExclusiveStream.Create(AFileName); try InternalPrint(LStream); finally FreeAndNil(LStream); end; end; function TIdLPR.GetJobId: String; begin Result := IndyFormat('%.3d', [FJobId]); {Do not Localize} end; procedure TIdLPR.SetJobId(const Value: String); var I: Integer; begin I := IndyStrToInt(Value); if I < 999 then begin FJobId := I; end; end; procedure TIdLPR.InternalPrint(Data: TStream); begin try if not Connected then begin Exit; end; Inc(FJobID); if FJobID > 999 then begin FJobID := 1; end; DoOnLPRStatus(psPrinting, JobID); try ControlFile.HostName := GStack.HostName except ControlFile.HostName := 'localhost'; {Do not Localize} end; // Receive a printer job IOHandler.Write(#02 + Queue + LF); CheckReply; // Receive control file IOHandler.Write(#02 + IntToStr(Length(GetControlData)) + ' cfA' + JobId + ControlFile.HostName + LF); {Do not Localize} CheckReply; // Send control file IOHandler.Write(GetControlData); IOHandler.Write(#0); CheckReply; // Send data file IOHandler.Write(#03 + IntToStr(Data.Size) + ' dfA' + JobId + ControlFile.HostName + LF); {Do not Localize} CheckReply; // Send data IOHandler.Write(Data); IOHandler.Write(#0); CheckReply; DoOnLPRStatus(psJobCompleted, JobID); except on E: Exception do begin DoOnLPRStatus(psError, E.Message); end; end; end; function TIdLPR.GetQueueState(const AShortFormat: Boolean = False; const AList : String = '') : String; {Do not Localize} begin DoOnLPRStatus(psGettingQueueState, AList); if AShortFormat then begin IOHandler.Write(#03 + Queue + ' ' + AList + LF) {Do not Localize} end else begin IOHandler.Write(#04 + Queue + ' ' + AList + LF); {Do not Localize} end; // This was the original code - problematic as this is more than one line // read until I close the connection // result:=ReadLn(LF); Result := IOHandler.AllData; DoOnLPRStatus(psGotQueueState, result); end; function TIdLPR.GetControlData: String; var Data: String; begin Data := ''; {Do not Localize} try // H - Host name Data := Data + 'H' + FControlFile.HostName + LF; {Do not Localize} // P - User identification Data := Data + 'P' + FControlFile.UserName + LF; {Do not Localize} // J - Job name for banner page if Length(FControlFile.JobName) > 0 then begin Data := Data + 'J' + FControlFile.JobName + LF; {Do not Localize} end else begin Data := Data + 'JcfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; //mail when printed if FControlFile.FMailWhenPrinted then begin Data := Data + 'M' + FControlFile.UserName + LF; {Do not Localize} end; case FControlFile.FFileFormat of ffCIF : // CalTech Intermediate Form begin Data := Data + 'cdfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffDVI : // DVI (TeX output). begin Data := Data + 'ddfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffFormattedText : //add formatting as needed to text file begin Data := Data + 'fdfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffPlot : // Berkeley Unix plot library begin Data := Data + 'gdfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffControlCharText : //text file with control charactors begin Data := Data + 'ldfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffDitroff : // ditroff output begin Data := Data + 'ndfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffPostScript : //Postscript output file begin Data := Data + 'odfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffPR : //'pr' format {Do not Localize} begin Data := Data + 'pdfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffFORTRAM : // FORTRAN carriage control begin Data := Data + 'rdfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffTroff : //Troff output begin Data := Data + 'ldfA' + JobId + FControlFile.HostName + LF; {Do not Localize} end; ffSunRaster : // Sun raster format file begin end; end; // U - Unlink data file Data := Data + 'UdfA' + JobId + FControlFile.HostName + LF; {Do not Localize} // N - Name of source file Data := Data + 'NcfA' + JobId + FControlFile.HostName + LF; {Do not Localize} if FControlFile.FFileFormat = ffFormattedText then begin if FControlFile.IndentCount > 0 then begin Data := Data + 'I' + IntToStr(FControlFile.IndentCount) + LF; {Do not Localize} end; if FControlFile.OutputWidth > 0 then begin Data := Data + 'W' + IntToStr(FControlFile.OutputWidth) + LF; {Do not Localize} end; end; if Length(FControlFile.BannerClass) > 0 then begin Data := Data + 'C' + FControlFile.BannerClass + LF; {Do not Localize} end; if FControlFile.BannerPage then begin Data := Data + 'L' + FControlFile.UserName + LF; {Do not Localize} end; if Length(FControlFile.TroffRomanFont) > 0 then begin Data := Data + '1' + FControlFile.TroffRomanFont + LF; {Do not Localize} end; if Length(FControlFile.TroffItalicFont) > 0 then begin Data := Data + '2' + FControlFile.TroffItalicFont + LF; {Do not Localize} end; if Length(FControlFile.TroffBoldFont) > 0 then begin Data := Data + '3' + FControlFile.TroffBoldFont + LF; {Do not Localize} end; if Length(FControlFile.TroffSpecialFont) > 0 then begin Data := Data + '4' + FControlFile.TroffSpecialFont + LF; {Do not Localize} end; Result := Data; except Result := 'error'; {Do not Localize} end; end; procedure TIdLPR.SeTIdLPRControlFile(const Value: TIdLPRControlFile); begin FControlFile.Assign(Value); end; destructor TIdLPR.Destroy; begin FreeAndNil(FControlFile); inherited Destroy; end; procedure TIdLPR.PrintWaitingJobs; begin try DoOnLPRStatus(psPrintingWaitingJobs, ''); {Do not Localize} IOHandler.Write(#03 + Queue + LF); CheckReply; DoOnLPRStatus(psPrintedWaitingJobs, ''); {Do not Localize} except on E: Exception do begin DoOnLPRStatus(psError, E.Message); end; end; end; procedure TIdLPR.RemoveJobList(const AList: String; const AAsRoot: Boolean = False); begin try DoOnLPRStatus(psDeletingJobs, JobID); if AAsRoot then begin {Only root can delete other people's print jobs} {Do not Localize} IOHandler.Write(#05 + Queue + ' root ' + AList + LF); {Do not Localize} end else begin IOHandler.Write(#05 + Queue + ' ' + ControlFile.UserName + ' ' + AList + LF); {Do not Localize} end; CheckReply; DoOnLPRStatus(psJobsDeleted, JobID); except on E: Exception do begin DoOnLPRStatus(psError, E.Message); end; end; end; procedure TIdLPR.CheckReply; var Ret : Byte; begin Ret := IOHandler.ReadByte; if Ret <> $00 then begin raise EIdLPRErrorException.CreateFmt(RSLPRError, [Integer(Ret), JobID]); end; end; procedure TIdLPR.DoOnLPRStatus(const AStatus: TIdLPRStatus; const AStatusText: String); begin if Assigned(FOnLPRStatus) then begin FOnLPRStatus(Self, AStatus, AStatusText); end; end; { TIdLPRControlFile } procedure TIdLPRControlFile.Assign(Source: TPersistent); var cnt : TIdLPRControlFile; begin if Source is TIdLPRControlFile then begin cnt := Source as TIdLPRControlFile; FBannerClass := cnt.BannerClass; FIndentCount := cnt.IndentCount; FJobName := cnt.JobName; FBannerPage := cnt.BannerPage; FUserName := cnt.UserName; FOutputWidth := cnt.OutputWidth; FFileFormat := cnt.FileFormat; FTroffRomanFont := cnt.TroffRomanFont; FTroffItalicFont := cnt.TroffItalicFont; FTroffBoldFont := cnt.TroffBoldFont; FTroffSpecialFont := cnt.TroffSpecialFont; FMailWhenPrinted := cnt.MailWhenPrinted; end else begin inherited Assign(Source); end; end; constructor TIdLPRControlFile.Create; begin inherited Create; try HostName := GStack.HostName; except HostName := RSLPRUnknown; end; FFileFormat := DEF_FILEFORMAT; FIndentCount := DEF_INDENTCOUNT; FBannerPage := DEF_BANNERPAGE; FOutputWidth := DEF_OUTPUTWIDTH; end; end.