IdLPR.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.12 7/24/04 12:56:14 PM RLebeau
  18. Compiler fix for Print(TIdBytes)
  19. Rev 1.11 7/23/04 7:15:16 PM RLebeau
  20. Added extra exception handling to various Print...() methods
  21. Rev 1.10 2004.05.20 11:36:50 AM czhower
  22. IdStreamVCL
  23. Rev 1.9 2004.03.03 11:54:32 AM czhower
  24. IdStream change
  25. Rev 1.8 2004.02.03 5:43:56 PM czhower
  26. Name changes
  27. Rev 1.7 1/21/2004 3:11:22 PM JPMugaas
  28. InitComponent
  29. Rev 1.6 10/24/2003 02:54:52 PM JPMugaas
  30. These should now work with the new code.
  31. Rev 1.5 2003.10.24 10:43:10 AM czhower
  32. TIdSTream to dos
  33. Rev 1.4 2003.10.12 4:04:00 PM czhower
  34. compile todos
  35. Rev 1.3 2/24/2003 09:07:26 PM JPMugaas
  36. Rev 1.2 2/6/2003 03:18:08 AM JPMugaas
  37. Updated components that compile with Indy 10.
  38. Rev 1.1 12/6/2002 05:30:18 PM JPMugaas
  39. Now decend from TIdTCPClientCustom instead of TIdTCPClient.
  40. Rev 1.0 11/13/2002 07:56:22 AM JPMugaas
  41. 27.07. rewrite component for integration
  42. in Indy core library
  43. }
  44. unit IdLPR;
  45. {
  46. Indy Line Print Remote TIdLPR
  47. Version 9.1.0
  48. Original author Mario Mueller
  49. home: www.hemasoft.de
  50. mail: [email protected]
  51. }
  52. interface
  53. {$i IdCompilerDefines.inc}
  54. uses
  55. Classes,
  56. IdAssignedNumbers, IdGlobal, IdException, IdTCPClient,
  57. IdComponent;
  58. type
  59. TIdLPRFileFormat =
  60. (ffCIF, // CalTech Intermediate Form
  61. ffDVI, // DVI (TeX output).
  62. ffFormattedText, //add formatting as needed to text file
  63. ffPlot, // Berkeley Unix plot library
  64. ffControlCharText, //text file with control charactors
  65. ffDitroff, // ditroff output
  66. ffPostScript, //Postscript output file
  67. ffPR,//'pr' format {Do not Localize}
  68. ffFORTRAM, // FORTRAN carriage control
  69. ffTroff, //Troff output
  70. ffSunRaster); // Sun raster format file
  71. const
  72. DEF_FILEFORMAT = ffControlCharText;
  73. DEF_INDENTCOUNT = 0;
  74. DEF_BANNERPAGE = False;
  75. DEF_OUTPUTWIDTH = 0;
  76. DEF_MAILWHENPRINTED = False;
  77. type
  78. TIdLPRControlFile = class(TPersistent)
  79. protected
  80. FBannerClass: String; // 'C' {Do not Localize}
  81. FHostName: String; // 'H' {Do not Localize}
  82. FIndentCount: Integer; // 'I' {Do not Localize}
  83. FJobName: String; // 'J' {Do not Localize}
  84. FBannerPage: Boolean; // 'L' {Do not Localize}
  85. FUserName: String; // 'P' {Do not Localize}
  86. FOutputWidth: Integer; // 'W' {Do not Localize}
  87. FFileFormat : TIdLPRFileFormat;
  88. FTroffRomanFont : String; //substitue the Roman font with the font in file
  89. FTroffItalicFont : String;//substitue the Italic font with the font in file
  90. FTroffBoldFont : String; //substitue the bold font with the font in file
  91. FTroffSpecialFont : String; //substitue the special font with the font
  92. //in this file
  93. FMailWhenPrinted : Boolean; //mail me when you have printed the job
  94. public
  95. constructor Create;
  96. procedure Assign(Source: TPersistent); override;
  97. property HostName: String read FHostName write FHostName;
  98. published
  99. property BannerClass: String read FBannerClass write FBannerClass;
  100. property IndentCount: Integer read FIndentCount write FIndentCount default DEF_INDENTCOUNT;
  101. property JobName: String read FJobName write FJobName;
  102. property BannerPage: Boolean read FBannerPage write FBannerPage default DEF_BANNERPAGE;
  103. property UserName: String read FUserName write FUserName;
  104. property OutputWidth: Integer read FOutputWidth write FOutputWidth default DEF_OUTPUTWIDTH;
  105. property FileFormat: TIdLPRFileFormat read FFileFormat write FFileFormat default DEF_FILEFORMAT;
  106. {font data }
  107. property TroffRomanFont : String read FTroffRomanFont write FTroffRomanFont;
  108. property TroffItalicFont : String read FTroffItalicFont write FTroffItalicFont;
  109. property TroffBoldFont : String read FTroffBoldFont write FTroffBoldFont;
  110. property TroffSpecialFont : String read FTroffSpecialFont write FTroffSpecialFont;
  111. {misc}
  112. property MailWhenPrinted : Boolean read FMailWhenPrinted write FMailWhenPrinted default DEF_MAILWHENPRINTED;
  113. end;
  114. type
  115. TIdLPRStatus = (psPrinting, psJobCompleted, psError, psGettingQueueState,
  116. psGotQueueState, psDeletingJobs, psJobsDeleted, psPrintingWaitingJobs,
  117. psPrintedWaitingJobs);
  118. type
  119. TIdLPRStatusEvent = procedure(ASender: TObject;
  120. const AStatus: TIdLPRStatus;
  121. const AStatusText: String) of object;
  122. type
  123. TIdLPR = class(TIdTCPClientCustom)
  124. protected
  125. FOnLPRStatus: TIdLPRStatusEvent;
  126. FQueue: String;
  127. FJobId: Integer;
  128. FControlFile: TIdLPRControlFile;
  129. procedure DoOnLPRStatus(const AStatus: TIdLPRStatus;
  130. const AStatusText: String);
  131. procedure SeTIdLPRControlFile(const Value: TIdLPRControlFile);
  132. procedure CheckReply;
  133. function GetJobId: String;
  134. procedure SetJobId(const Value: String);
  135. procedure InternalPrint(Data: TStream);
  136. function GetControlData: String;
  137. procedure InitComponent; override;
  138. public
  139. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  140. constructor Create(AOwner: TComponent); reintroduce; overload;
  141. {$ENDIF}
  142. destructor Destroy; override;
  143. procedure Connect; override;
  144. procedure Print(const AText: String); overload;
  145. procedure Print(const ABuffer: TIdBytes); overload;
  146. procedure PrintFile(const AFileName: String);
  147. function GetQueueState(const AShortFormat: Boolean = False; const AList : String = '') : String; {Do not Localize}
  148. procedure PrintWaitingJobs;
  149. procedure RemoveJobList(const AList: String; const AAsRoot: Boolean = False);
  150. property JobId: String read GetJobId write SetJobId;
  151. published
  152. property Queue: String read FQueue write FQueue;
  153. property ControlFile: TIdLPRControlFile read FControlFile write SeTIdLPRControlFile;
  154. property Host;
  155. property Port default IdPORT_LPD;
  156. property OnLPRStatus: TIdLPRStatusEvent read FOnLPRStatus write FOnLPRStatus;
  157. end;
  158. type
  159. EIdLPRErrorException = class(EIdException);
  160. implementation
  161. uses
  162. IdGlobalProtocols, IdResourceStringsProtocols, IdStack, IdStackConsts,
  163. SysUtils;
  164. { TIdLPR }
  165. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  166. constructor TIdLPR.Create(AOwner: TComponent);
  167. begin
  168. inherited Create(AOwner);
  169. end;
  170. {$ENDIF}
  171. procedure TIdLPR.InitComponent;
  172. begin
  173. inherited InitComponent;
  174. Port := IdPORT_LPD;
  175. Queue := 'pr1'; {Do not Localize}
  176. FJobId := 1;
  177. FControlFile := TIdLPRControlFile.Create;
  178. // Restriction in RFC 1179
  179. // The source port must be in the range 721 to 731, inclusive.
  180. BoundPortMin := 721;
  181. BoundPortMax := 731;
  182. end;
  183. procedure TIdLPR.Connect;
  184. var
  185. LPort: TIdPort;
  186. begin
  187. // RLebeau 3/7/2010: there is a problem on Windows where sometimes it will
  188. // not raise a WSAEADDRINUSE error in TIdSocketHandle.TryBind(), but will
  189. // delay it until TIdSocketHandle.Connect() instead. So we will loop here
  190. // to force a Connect() on each port, rather than let TIdSocketHandle do
  191. // the looping in BindPortReserved(). If this logic proves useful in other
  192. // protocols, we can move it into TIdSocketHandle later on...
  193. // AWinkelsdorf 3/9/2010: Implemented, adjusted to use BoundPortMax and
  194. // BoundPortMin
  195. // looping backwards because that is what TIdSocketHandle.BindPortReserved() does
  196. for LPort := BoundPortMax downto BoundPortMin do
  197. begin
  198. BoundPort := LPort;
  199. try
  200. inherited Connect;
  201. Exit;
  202. except
  203. on E: EIdCouldNotBindSocket do begin end;
  204. on E: EIdSocketError do begin
  205. if E.LastError <> Id_WSAEADDRINUSE then begin
  206. raise;
  207. end;
  208. // Socket already in use, cleanup and try again with the next
  209. Disconnect;
  210. end;
  211. end;
  212. end;
  213. // no local ports could be bound successfully
  214. raise EIdCanNotBindPortInRange.CreateFmt(RSCannotBindRange, [BoundPortMin, BoundPortMax]);
  215. end;
  216. procedure TIdLPR.Print(const AText: String);
  217. var
  218. LStream: TStream;
  219. LEncoding: IIdTextEncoding;
  220. begin
  221. LStream := TMemoryStream.Create;
  222. try
  223. LEncoding := IndyTextEncoding_8Bit;
  224. WriteStringToStream(LStream, AText, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
  225. LEncoding := nil;
  226. LStream.Position := 0;
  227. InternalPrint(LStream);
  228. finally
  229. FreeAndNil(LStream);
  230. end;
  231. end;
  232. procedure TIdLPR.Print(const ABuffer: TIdBytes);
  233. var
  234. LStream: TMemoryStream;
  235. begin
  236. LStream := TMemoryStream.Create;
  237. try
  238. WriteTIdBytesToStream(LStream, ABuffer);
  239. LStream.Position := 0;
  240. InternalPrint(LStream);
  241. finally
  242. FreeAndNil(LStream);
  243. end;
  244. end;
  245. procedure TIdLPR.PrintFile(const AFileName: String);
  246. var
  247. LStream: TIdReadFileExclusiveStream;
  248. p: Integer;
  249. begin
  250. p := RPos(GPathDelim, AFileName);
  251. ControlFile.JobName := Copy(AFileName, p+1, Length(AFileName)-p);
  252. LStream := TIdReadFileExclusiveStream.Create(AFileName);
  253. try
  254. InternalPrint(LStream);
  255. finally
  256. FreeAndNil(LStream);
  257. end;
  258. end;
  259. function TIdLPR.GetJobId: String;
  260. begin
  261. Result := IndyFormat('%.3d', [FJobId]); {Do not Localize}
  262. end;
  263. procedure TIdLPR.SetJobId(const Value: String);
  264. var
  265. I: Integer;
  266. begin
  267. I := IndyStrToInt(Value);
  268. if I < 999 then begin
  269. FJobId := I;
  270. end;
  271. end;
  272. procedure TIdLPR.InternalPrint(Data: TStream);
  273. begin
  274. try
  275. if not Connected then begin
  276. Exit;
  277. end;
  278. Inc(FJobID);
  279. if FJobID > 999 then begin
  280. FJobID := 1;
  281. end;
  282. DoOnLPRStatus(psPrinting, JobID);
  283. try
  284. ControlFile.HostName := GStack.HostName
  285. except
  286. ControlFile.HostName := 'localhost'; {Do not Localize}
  287. end;
  288. // Receive a printer job
  289. IOHandler.Write(#02 + Queue + LF);
  290. CheckReply;
  291. // Receive control file
  292. IOHandler.Write(#02 + IntToStr(Length(GetControlData)) + ' cfA' + JobId + ControlFile.HostName + LF); {Do not Localize}
  293. CheckReply;
  294. // Send control file
  295. IOHandler.Write(GetControlData);
  296. IOHandler.Write(#0);
  297. CheckReply;
  298. // Send data file
  299. IOHandler.Write(#03 + IntToStr(Data.Size) + ' dfA' + JobId + ControlFile.HostName + LF); {Do not Localize}
  300. CheckReply;
  301. // Send data
  302. IOHandler.Write(Data);
  303. IOHandler.Write(#0);
  304. CheckReply;
  305. DoOnLPRStatus(psJobCompleted, JobID);
  306. except
  307. on E: Exception do begin
  308. DoOnLPRStatus(psError, E.Message);
  309. end;
  310. end;
  311. end;
  312. function TIdLPR.GetQueueState(const AShortFormat: Boolean = False; const AList : String = '') : String; {Do not Localize}
  313. begin
  314. DoOnLPRStatus(psGettingQueueState, AList);
  315. if AShortFormat then begin
  316. IOHandler.Write(#03 + Queue + ' ' + AList + LF) {Do not Localize}
  317. end else begin
  318. IOHandler.Write(#04 + Queue + ' ' + AList + LF); {Do not Localize}
  319. end;
  320. // This was the original code - problematic as this is more than one line
  321. // read until I close the connection
  322. // result:=ReadLn(LF);
  323. Result := IOHandler.AllData;
  324. DoOnLPRStatus(psGotQueueState, result);
  325. end;
  326. function TIdLPR.GetControlData: String;
  327. var
  328. Data: String;
  329. begin
  330. Data := ''; {Do not Localize}
  331. try
  332. // H - Host name
  333. Data := Data + 'H' + FControlFile.HostName + LF; {Do not Localize}
  334. // P - User identification
  335. Data := Data + 'P' + FControlFile.UserName + LF; {Do not Localize}
  336. // J - Job name for banner page
  337. if Length(FControlFile.JobName) > 0 then begin
  338. Data := Data + 'J' + FControlFile.JobName + LF; {Do not Localize}
  339. end else begin
  340. Data := Data + 'JcfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  341. end;
  342. //mail when printed
  343. if FControlFile.FMailWhenPrinted then begin
  344. Data := Data + 'M' + FControlFile.UserName + LF; {Do not Localize}
  345. end;
  346. case FControlFile.FFileFormat of
  347. ffCIF : // CalTech Intermediate Form
  348. begin
  349. Data := Data + 'cdfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  350. end;
  351. ffDVI : // DVI (TeX output).
  352. begin
  353. Data := Data + 'ddfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  354. end;
  355. ffFormattedText : //add formatting as needed to text file
  356. begin
  357. Data := Data + 'fdfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  358. end;
  359. ffPlot : // Berkeley Unix plot library
  360. begin
  361. Data := Data + 'gdfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  362. end;
  363. ffControlCharText : //text file with control charactors
  364. begin
  365. Data := Data + 'ldfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  366. end;
  367. ffDitroff : // ditroff output
  368. begin
  369. Data := Data + 'ndfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  370. end;
  371. ffPostScript : //Postscript output file
  372. begin
  373. Data := Data + 'odfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  374. end;
  375. ffPR : //'pr' format {Do not Localize}
  376. begin
  377. Data := Data + 'pdfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  378. end;
  379. ffFORTRAM : // FORTRAN carriage control
  380. begin
  381. Data := Data + 'rdfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  382. end;
  383. ffTroff : //Troff output
  384. begin
  385. Data := Data + 'ldfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  386. end;
  387. ffSunRaster : // Sun raster format file
  388. begin
  389. end;
  390. end;
  391. // U - Unlink data file
  392. Data := Data + 'UdfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  393. // N - Name of source file
  394. Data := Data + 'NcfA' + JobId + FControlFile.HostName + LF; {Do not Localize}
  395. if FControlFile.FFileFormat = ffFormattedText then begin
  396. if FControlFile.IndentCount > 0 then begin
  397. Data := Data + 'I' + IntToStr(FControlFile.IndentCount) + LF; {Do not Localize}
  398. end;
  399. if FControlFile.OutputWidth > 0 then begin
  400. Data := Data + 'W' + IntToStr(FControlFile.OutputWidth) + LF; {Do not Localize}
  401. end;
  402. end;
  403. if Length(FControlFile.BannerClass) > 0 then begin
  404. Data := Data + 'C' + FControlFile.BannerClass + LF; {Do not Localize}
  405. end;
  406. if FControlFile.BannerPage then begin
  407. Data := Data + 'L' + FControlFile.UserName + LF; {Do not Localize}
  408. end;
  409. if Length(FControlFile.TroffRomanFont) > 0 then begin
  410. Data := Data + '1' + FControlFile.TroffRomanFont + LF; {Do not Localize}
  411. end;
  412. if Length(FControlFile.TroffItalicFont) > 0 then begin
  413. Data := Data + '2' + FControlFile.TroffItalicFont + LF; {Do not Localize}
  414. end;
  415. if Length(FControlFile.TroffBoldFont) > 0 then begin
  416. Data := Data + '3' + FControlFile.TroffBoldFont + LF; {Do not Localize}
  417. end;
  418. if Length(FControlFile.TroffSpecialFont) > 0 then begin
  419. Data := Data + '4' + FControlFile.TroffSpecialFont + LF; {Do not Localize}
  420. end;
  421. Result := Data;
  422. except
  423. Result := 'error'; {Do not Localize}
  424. end;
  425. end;
  426. procedure TIdLPR.SeTIdLPRControlFile(const Value: TIdLPRControlFile);
  427. begin
  428. FControlFile.Assign(Value);
  429. end;
  430. destructor TIdLPR.Destroy;
  431. begin
  432. FreeAndNil(FControlFile);
  433. inherited Destroy;
  434. end;
  435. procedure TIdLPR.PrintWaitingJobs;
  436. begin
  437. try
  438. DoOnLPRStatus(psPrintingWaitingJobs, ''); {Do not Localize}
  439. IOHandler.Write(#03 + Queue + LF);
  440. CheckReply;
  441. DoOnLPRStatus(psPrintedWaitingJobs, ''); {Do not Localize}
  442. except
  443. on E: Exception do begin
  444. DoOnLPRStatus(psError, E.Message);
  445. end;
  446. end;
  447. end;
  448. procedure TIdLPR.RemoveJobList(const AList: String; const AAsRoot: Boolean = False);
  449. begin
  450. try
  451. DoOnLPRStatus(psDeletingJobs, JobID);
  452. if AAsRoot then begin
  453. {Only root can delete other people's print jobs} {Do not Localize}
  454. IOHandler.Write(#05 + Queue + ' root ' + AList + LF); {Do not Localize}
  455. end else begin
  456. IOHandler.Write(#05 + Queue + ' ' + ControlFile.UserName + ' ' + AList + LF); {Do not Localize}
  457. end;
  458. CheckReply;
  459. DoOnLPRStatus(psJobsDeleted, JobID);
  460. except
  461. on E: Exception do begin
  462. DoOnLPRStatus(psError, E.Message);
  463. end;
  464. end;
  465. end;
  466. procedure TIdLPR.CheckReply;
  467. var
  468. Ret : Byte;
  469. begin
  470. Ret := IOHandler.ReadByte;
  471. if Ret <> $00 then begin
  472. raise EIdLPRErrorException.CreateFmt(RSLPRError, [Integer(Ret), JobID]);
  473. end;
  474. end;
  475. procedure TIdLPR.DoOnLPRStatus(const AStatus: TIdLPRStatus; const AStatusText: String);
  476. begin
  477. if Assigned(FOnLPRStatus) then begin
  478. FOnLPRStatus(Self, AStatus, AStatusText);
  479. end;
  480. end;
  481. { TIdLPRControlFile }
  482. procedure TIdLPRControlFile.Assign(Source: TPersistent);
  483. var
  484. cnt : TIdLPRControlFile;
  485. begin
  486. if Source is TIdLPRControlFile then
  487. begin
  488. cnt := Source as TIdLPRControlFile;
  489. FBannerClass := cnt.BannerClass;
  490. FIndentCount := cnt.IndentCount;
  491. FJobName := cnt.JobName;
  492. FBannerPage := cnt.BannerPage;
  493. FUserName := cnt.UserName;
  494. FOutputWidth := cnt.OutputWidth;
  495. FFileFormat := cnt.FileFormat;
  496. FTroffRomanFont := cnt.TroffRomanFont;
  497. FTroffItalicFont := cnt.TroffItalicFont;
  498. FTroffBoldFont := cnt.TroffBoldFont;
  499. FTroffSpecialFont := cnt.TroffSpecialFont;
  500. FMailWhenPrinted := cnt.MailWhenPrinted;
  501. end else begin
  502. inherited Assign(Source);
  503. end;
  504. end;
  505. constructor TIdLPRControlFile.Create;
  506. begin
  507. inherited Create;
  508. try
  509. HostName := GStack.HostName;
  510. except
  511. HostName := RSLPRUnknown;
  512. end;
  513. FFileFormat := DEF_FILEFORMAT;
  514. FIndentCount := DEF_INDENTCOUNT;
  515. FBannerPage := DEF_BANNERPAGE;
  516. FOutputWidth := DEF_OUTPUTWIDTH;
  517. end;
  518. end.