/components/printers/unix/cupsprinters.inc

http://github.com/graemeg/lazarus · Pascal · 1398 lines · 1104 code · 142 blank · 152 comment · 162 complexity · a78800d93f61bef146b50b7075d2f012 MD5 · raw file

  1. {%MainUnit ../osprinters.pas}
  2. {$IFDEF DebugCUPS}
  3. {$DEFINE LogPrintoutFile}
  4. {$ENDIF}
  5. {**************************************************************
  6. Implementation for cupsprinter
  7. ***************************************************************}
  8. uses
  9. {%H-}udlgSelectPrinter, // used to compile it on this target
  10. {%H-}udlgpropertiesprinter, // used to compile it on this target
  11. FileUtil, LazFileUtils;
  12. //Return always 72 because, PostScript it's 72 only
  13. function TCUPSPrinter.GetXDPI: Integer;
  14. begin
  15. Result:=InternalGetResolution(True);
  16. end;
  17. //Return always 72 because, PostScript it's 72 only
  18. function TCUPSPrinter.GetYDPI: Integer;
  19. begin
  20. Result:=InternalGetResolution(False);
  21. end;
  22. procedure TCUPSPrinter.DoEnumBins(Lst: TStrings);
  23. var
  24. choice: Pppd_choice_t;
  25. Option: Pppd_option_t;
  26. c: Integer;
  27. begin
  28. Lst.Clear;
  29. if CupsPPD<>nil then
  30. begin
  31. Option := ppdFindOption(CupsPPD, PChar('InputSlot'));
  32. if Option<>nil then
  33. begin
  34. Choice := Option^.choices;
  35. c := 0;
  36. while (Choice<>nil) and (c<Option^.num_choices) do
  37. begin
  38. lst.AddObject(Choice^.text, TObject(Choice));
  39. inc(choice);
  40. inc(c);
  41. end;
  42. end;
  43. end;
  44. end;
  45. function TCUPSPrinter.DoGetDefaultBinName: string;
  46. var
  47. Option: Pppd_option_t;
  48. Choice: pppd_choice_t;
  49. begin
  50. Result:=inherited DoGetDefaultBinName;
  51. if CupsPPD<>nil then
  52. begin
  53. Option := ppdFindOption(CupsPPD, 'InputSlot');
  54. if Option<>nil then
  55. begin
  56. choice := PPDOptionChoiceFrom('InputSlot', Option^.defchoice, true);
  57. if choice<>nil then
  58. result := choice^.text;
  59. end;
  60. end;
  61. end;
  62. function TCUPSPrinter.DoGetBinName: string;
  63. var
  64. Choice: pppd_choice_t;
  65. begin
  66. result := cupsGetOption('InputSlot');
  67. if result<>'' then
  68. begin
  69. Choice := PPDOptionChoiceFrom('InputSlot', result, true);
  70. if Choice<>nil then
  71. result := Choice^.text
  72. else
  73. result := '';
  74. end;
  75. if result='' then
  76. result := doGetDefaultBinName
  77. end;
  78. procedure TCUPSPrinter.DoSetBinName(aName: string);
  79. var
  80. Choice: pppd_choice_t;
  81. begin
  82. Choice := PPDOptionChoiceFrom('InputSlot', aName, false);
  83. if Choice<>nil then
  84. cupsAddOption('InputSlot', choice^.choice)
  85. else
  86. inherited doSetBinName(aName); // handle input slot not found
  87. end;
  88. //write count bytes from buffer to raw mode stream
  89. function TCUPSPrinter.Write(const Buffer; Count: Integer; var Written: Integer
  90. ): Boolean;
  91. begin
  92. result := False;
  93. CheckRawMode(True);
  94. if not Assigned(FRawModeStream) then
  95. FRawModeStream := TMemoryStream.Create;
  96. Written := FRawModeStream.Write(Buffer, Count);
  97. Result := True;
  98. end;
  99. constructor TCUPSPrinter.Create;
  100. begin
  101. inherited Create;
  102. fcupsPrinters:=nil;
  103. fcupsPrinter :=nil;
  104. fcupsHttp :=nil;
  105. fcupsPPD :=nil;
  106. fcupsOptions :=nil;
  107. fcupsNumOpts :=0;
  108. FRawModeStream := nil;
  109. FCupsPapersCount := -1;
  110. end;
  111. procedure TCUPSPrinter.DoDestroy;
  112. begin
  113. if assigned(fRawModeStream) then
  114. fRawModeStream.Free;
  115. FreeOptions;
  116. if Assigned(fcupsHttp) then
  117. httpClose(fcupsHttp);
  118. inherited DoDestroy;
  119. end;
  120. procedure TCUPSPrinter.FreeOptions;
  121. begin
  122. if Assigned(fcupsOptions) then
  123. cupsFreeOptions(fcupsNumOpts,fcupsOptions);
  124. fcupsNumOpts:=0;
  125. fcupsOptions:=nil;
  126. FStates := [];
  127. end;
  128. procedure TCUPSPrinter.cupsAddOption(aName,aValue: string);
  129. begin
  130. if not CUPSLibInstalled then Exit;
  131. fcupsNumOpts:=cupsdyn.cupsAddOption(PChar(aName),PChar(aValue),fcupsNumOpts,
  132. @fcupsOptions);
  133. if (AName='PageSize') then
  134. begin
  135. Exclude(FStates,cpsPaperNameValid);
  136. Exclude(FStates,cpsPaperRectValid);
  137. end;
  138. {$IFDEF DebugCUPS}
  139. DebugLn('TCUPSPrinter.cupsAddOption AName=%s AValue=%s',[AName,AValue]);
  140. {$ENDIF}
  141. end;
  142. //Return the value of option set for the selected printer
  143. function TCUPSPrinter.cupsGetOption(aKeyWord: string): String;
  144. begin
  145. Result:='';
  146. if not CUPSLibInstalled then Exit;
  147. if (Printers.Count>0) then
  148. begin
  149. if not Assigned(fcupsOptions) then
  150. SetOptionsOfPrinter;
  151. Result:=cupsdyn.cupsGetOption(PChar(aKeyWord),fcupsNumOpts,fcupsOptions);
  152. end;
  153. end;
  154. function TCUPSPrinter.CopyOptions(out AOptions: Pcups_option_t): Integer;
  155. var
  156. i: Integer;
  157. begin
  158. AOptions := nil;
  159. Result := 0;
  160. for i:=0 to fcupsNumOpts-1 do
  161. Result := cupsdyn.cupsAddOption(fcupsOptions[i].name,fcupsOptions[i].value,
  162. Result,@AOptions);
  163. end;
  164. procedure TCUPSPrinter.MergeOptions(const AOptions:Pcups_option_t; const n:Integer);
  165. var
  166. i: Integer;
  167. begin
  168. for i:=0 to n-1 do
  169. if
  170. // always merge some known options
  171. (strcomp('job-sheets', AOptions[i].name)=0) or
  172. // check if ppd option value is valid
  173. IsOptionValueValid(AOptions[i].name, AOptions[i].value)
  174. then
  175. cupsAddOption(AOptions[i].name, AOptions[i].value);
  176. cupsFreeOptions(n, AOptions);
  177. end;
  178. function TCUPSPrinter.GetResolutionOption: string;
  179. var
  180. L1,L2: TStringlist;
  181. i: Integer;
  182. begin
  183. Result := Self.cupsGetOption('Resolution');
  184. if Result='' then
  185. begin
  186. // get resolution from ppd
  187. Result := GetPPDAttribute('DefaultResolution');
  188. if Result='' then
  189. begin
  190. // try grouped options
  191. L1 := TStringList.Create;
  192. L2 := TStringList.Create;
  193. try
  194. i := EnumPPDChoice(L1,'Resolution',L2);
  195. if i>=0 then
  196. Result := L2[i]
  197. finally
  198. L2.Free;
  199. L1.Free;
  200. end;
  201. end;
  202. end;
  203. end;
  204. procedure TCUPSPrinter.DebugOptions(AOPtions:Pcups_option_t=nil; n:Integer=0);
  205. var
  206. i: Integer;
  207. begin
  208. if (Printers.Count>0) and CUPSLibInstalled and (fcupsPrinter<>nil) then
  209. begin
  210. DebugLn('**************************************************');
  211. if AOptions=nil then
  212. begin
  213. AOptions:= fcupsOptions;
  214. n := fcupsNumOpts;
  215. end;
  216. DebugLn('Printer "%s" Number of Options %d',[fcupsPrinter^.Name,n]);
  217. for i:=0 to n-1 do
  218. DebugLn('name="%s" value="%s"',[AOptions[i].name,AOptions[i].value]);
  219. DebugLn('**************************************************');
  220. end else
  221. DebugLn('DebugOptions: There are no valid printers');
  222. end;
  223. procedure TCUPSPrinter.DoCupsConnect;
  224. begin
  225. if not assigned(fcupsHttp) then
  226. begin
  227. if not CUPSLibInstalled then Exit;
  228. fcupsHttp:=httpConnect(cupsServer(),ippPort());
  229. if not Assigned(fcupsHttp) then
  230. raise Exception.Create('Unable to contact server: '+GetLastError);
  231. end;
  232. end;
  233. function TCUPSPrinter.CupsPapersListValid: boolean;
  234. var
  235. Lst: TStringlist;
  236. begin
  237. if fCupsPapersCount<=0 then begin
  238. // paper list no exists or
  239. // paper list is not enumerated yet, try it now.
  240. Lst := TStringlist.Create;
  241. try
  242. DoEnumPapers(Lst);
  243. finally
  244. Lst.Free;
  245. end;
  246. end;
  247. result := fCupsPapersCount>0;
  248. end;
  249. function TCUPSPrinter.InternalGetResolution(ForX: boolean): Integer;
  250. procedure ParseResolution(s:string);
  251. var
  252. a,b: Integer;
  253. begin
  254. if s<>'' then begin
  255. s := uppercase(s);
  256. a := pos('X', S);
  257. b := pos('D', S);
  258. if b=0 then
  259. b := Length(S)
  260. else
  261. dec(b);
  262. if a>0 then begin
  263. // NNNXMMMDPI (or NNN X MMM DPI)
  264. FCachedResolution.x := StrToIntDef(trim(copy(S,1,a-1)), 0);
  265. FCAchedResolution.y := StrToIntDef(trim(copy(S,a+1,b)), 0);
  266. end else begin
  267. // NNNDPI (or NNN DPI);
  268. FCachedResolution.x := StrToIntDef(trim(copy(S,1,b)), 0);
  269. FCachedResolution.y := FCachedResolution.x;
  270. end;
  271. end;
  272. end;
  273. begin
  274. if not (cpsResolutionValid in FStates) then begin
  275. // check user defined resolution
  276. FCachedResolution.x := 0;
  277. FCachedResolution.y := 0;
  278. ParseResolution(GetResolutionOption);
  279. if (FCachedResolution.x=0) or (FCachedResolution.y=0) then
  280. begin
  281. FCachedResolution.x := 300;
  282. FCachedResolution.y := 300;
  283. end;
  284. include(FStates, cpsResolutionValid);
  285. end;
  286. if ForX then
  287. result := FCachedResolution.X
  288. else
  289. result := FCachedResolution.Y;
  290. end;
  291. {$IFDEF DebugCUPS}
  292. procedure TCUPSPrinter.DebugCapabilities;
  293. var
  294. flags: Integer;
  295. procedure DumpCap(const aFlag: integer; const flagMsg, Desc: string; invert: boolean=false);
  296. begin
  297. if (invert and (aFlag and Flags=0)) or (not invert and (aFlag and Flags<>0)) then
  298. DebugLn(flagMsg, '(',Desc,')');
  299. end;
  300. begin
  301. flags := GetAttributeInteger('printer-type',CUPS_PRINTER_LOCAL);
  302. DebugLn('=== CAPABILITIES ===');
  303. DebugLn;
  304. DumpCap(CUPS_PRINTER_CLASS or CUPS_PRINTER_REMOTE, 'CUPS_PRINTER_LOCAL ', 'Local printer or class ', true);
  305. DumpCap(CUPS_PRINTER_CLASS , 'CUPS_PRINTER_CLASS ', 'Printer class ');
  306. DumpCap(CUPS_PRINTER_REMOTE , 'CUPS_PRINTER_REMOTE ', 'Remote printer or class ');
  307. DumpCap(CUPS_PRINTER_BW , 'CUPS_PRINTER_BW ', 'Can do B&W printing ');
  308. DumpCap(CUPS_PRINTER_COLOR , 'CUPS_PRINTER_COLOR ', 'Can do color printing ');
  309. DumpCap(CUPS_PRINTER_DUPLEX , 'CUPS_PRINTER_DUPLEX ', 'Can do duplexing ');
  310. DumpCap(CUPS_PRINTER_STAPLE , 'CUPS_PRINTER_STAPLE ', 'Can staple output ');
  311. DumpCap(CUPS_PRINTER_COPIES , 'CUPS_PRINTER_COPIES ', 'Can do copies ');
  312. DumpCap(CUPS_PRINTER_COLLATE , 'CUPS_PRINTER_COLLATE ', 'Can collage copies ');
  313. DumpCap(CUPS_PRINTER_PUNCH , 'CUPS_PRINTER_PUNCH ', 'Can punch output ');
  314. DumpCap(CUPS_PRINTER_COVER , 'CUPS_PRINTER_COVER ', 'Can cover output ');
  315. DumpCap(CUPS_PRINTER_BIND , 'CUPS_PRINTER_BIND ', 'Can bind output ');
  316. DumpCap(CUPS_PRINTER_SORT , 'CUPS_PRINTER_SORT ', 'Can sort output ');
  317. DumpCap(CUPS_PRINTER_SMALL , 'CUPS_PRINTER_SMALL ', 'Can do Letter/Legal/A4 ');
  318. DumpCap(CUPS_PRINTER_MEDIUM , 'CUPS_PRINTER_MEDIUM ', 'Can do Tabloid/B/C/A3/A2 ');
  319. DumpCap(CUPS_PRINTER_LARGE , 'CUPS_PRINTER_LARGE ', 'Can do D/E/A1/A0 ');
  320. DumpCap(CUPS_PRINTER_VARIABLE , 'CUPS_PRINTER_VARIABLE ', 'Can do variable sizes ');
  321. DumpCap(CUPS_PRINTER_IMPLICIT , 'CUPS_PRINTER_IMPLICIT ', 'Implicit class ');
  322. DumpCap(CUPS_PRINTER_DEFAULT , 'CUPS_PRINTER_DEFAULT ', 'Default printer on network');
  323. end;
  324. procedure TCUPSPrinter.DebugPPD;
  325. const
  326. arruitypes:array[ppd_ui_t] of string[9] = ('boolean','pickone','pickmany');
  327. arrsection:array[ppd_section_t] of string[9] = ('any','document','exit','jcl','page','prolog');
  328. var
  329. i,j,k: Integer;
  330. AttrRoot : Ppppd_attr_t;
  331. Attr : Pppd_attr_t;
  332. Group : pppd_group_t;
  333. Option : Pppd_option_t;
  334. choices : Pppd_choice_t;
  335. function markchar(const AMark:char):char;
  336. begin
  337. if AMark=#1 then
  338. result := '*'
  339. else
  340. result := ' ';
  341. end;
  342. begin
  343. DebugLn;
  344. DebugLn('DebugPPD: ppdfile=',fCupsPPDName);
  345. if fcupsPPD=nil then
  346. begin
  347. DebugLn('No valid ppd file found');
  348. exit;
  349. end;
  350. DebugLn('=== HEADER ===');
  351. DebugLn;
  352. DebugLn(' model : %s', [fcupsPPD^.modelname]);
  353. DebugLn(' modelNumber : %d', [fcupsPPD^.model_number]);
  354. DebugLn(' manufacturer : %s', [fcupsPPD^.manufacturer]);
  355. DebugLn(' nickname : %s', [fcupsPPD^.nickname]);
  356. DebugLn(' shortnickname : %s', [fcupsPPD^.shortnickname]);
  357. DebugLn(' product : %s', [fcupsPPD^.product]);
  358. DebugLn(' attributes : %d Current=%d', [fcupsPPD^.num_attrs,fcupsPPD^.cur_attr]);
  359. DebugLn(' language_level : %d', [fcupsPPD^.language_level]);
  360. DebugLn(' lang_version : %s', [fcupsPPD^.lang_version]);
  361. DebugLn(' lang_encoding : %s', [fcupsPPD^.lang_encoding]);
  362. DebugLn(' landscape : %d', [fcupsPPD^.landscape]);
  363. DebugLn(' UI groups : %d', [fcupsPPD^.num_groups]);
  364. DebugLn(' Num Papers : %d', [fcupsPPD^.num_sizes]);
  365. DebugLn(' Num Attributes : %d', [fcupsPPD^.num_attrs]);
  366. DebugLn(' Num Constrains : %d', [fcupsPPD^.num_consts]);
  367. DebugLn;
  368. DebugLn('=== CUSTOM PAPER SUPPORT ===');
  369. DebugLn;
  370. DebugLn(' Custom Min 0 : %.2f',[fcupsPPD^.custom_min[0]]);
  371. DebugLn(' Custom Min 1 : %.2f',[fCupsPPD^.custom_min[1]]);
  372. DebugLn(' Custom Max 0 : %.2f',[fcupsPPD^.custom_max[0]]);
  373. DebugLn(' Custom Max 1 : %.2f',[fcupsPPD^.custom_max[1]]);
  374. with fcupsPPD^ do
  375. DebugLn(' Custom Margins : %.2f %.2f %.2f %.2f',
  376. [custom_margins[0],custom_margins[1],custom_margins[2],custom_margins[3]]);
  377. DebugLn;
  378. if fcupsPPD^.num_groups>0 then
  379. begin
  380. DebugLn('=== GROUPS ===');
  381. i := 0;
  382. Group := fCupsPPD^.groups;
  383. while (Group<>nil) and (i<fcupsPPD^.num_groups) do
  384. begin
  385. DebugLn('Group %d Name="%s" Text="%s" Options=%d SubGroups=%d',
  386. [i,Group^.name,Group^.text,Group^.num_options,Group^.num_subgroups]);
  387. j := 0;
  388. Option := group^.options;
  389. while j< group^.num_options do
  390. begin
  391. with Option^ do
  392. DebugLn(' Option %d Key="%s" Def="%s" Text="%s" UIType="%s" section="%s" Choices=%d',
  393. [j,keyword,defchoice,text,arruitypes[ui],arrsection[section],num_choices]);
  394. k := 0;
  395. Choices := Option^.choices;
  396. while k<Option^.num_choices do
  397. begin
  398. DebugLn(' Choice %2d %s Choice=%-20s Text="%s"',
  399. [k,MarkChar(Choices^.marked),Choices^.Choice,Choices^.Text]);
  400. inc(Choices);
  401. inc(k);
  402. end;
  403. inc(Option);
  404. inc(j);
  405. end;
  406. inc(Group);
  407. inc(i);
  408. end;
  409. end;
  410. DebugLn;
  411. if fcupsPPD^.num_attrs>0 then
  412. begin
  413. DebugLn('=== Attributes ===');
  414. i := 0;
  415. AttrRoot := fCupsPPD^.attrs;
  416. while (AttrRoot<>nil) and (i<fcupsPPD^.num_attrs) do
  417. begin
  418. Attr := AttrRoot^;
  419. if attr<>nil then
  420. DebugLn(' i=%d Name=%s Spec=%s Value=%s',[i,Attr^.Name,Attr^.Spec,Attr^.Value]);
  421. inc(i);
  422. inc(AttrRoot);
  423. end;
  424. end;
  425. end;
  426. {$ENDIF}
  427. //Print the file aFileName with a selected printer and options
  428. function TCUPSPrinter.PrintFile(aFileName: String): longint;
  429. var
  430. aPrinterName : string;
  431. begin
  432. Result:=-1;
  433. //debugln(['TCUPSPrinter.PrintFile START ',aFileName]);
  434. if aFileName='' then
  435. raise Exception.Create('TCUPSPrinter.PrintFile missing Filename');
  436. if not CUPSLibInstalled then Exit;
  437. aFileName:=ExpandFileNameUTF8(aFileName);
  438. if (Printers.Count>0) then
  439. begin
  440. if not Assigned(fcupsOptions) then
  441. SetOptionsOfPrinter;
  442. if Assigned(fcupsPrinter) then
  443. aPrinterName:=fcupsPrinter^.Name
  444. else
  445. aPrinterName:='';
  446. {$IFDEF DebugCUPS}
  447. DebugOptions;
  448. debugln(['TCUPSPrinter.PrintFile aPrinterName="',aPrinterName,'" aFileName="',aFileName,'" Size=',FileSizeUtf8(aFileName)]);
  449. {$ENDIF}
  450. Result:=cupsdyn.cupsPrintFile(PChar(aPrinterName),PChar(aFileName),
  451. PChar(Self.Title),
  452. fcupsNumOpts,fcupsOptions);
  453. end;
  454. end;
  455. function TCUPSPrinter.GetLastError: string;
  456. begin
  457. Result:=ippErrorString(cupsdyn.cupsLastError());
  458. end;
  459. function TCUPSPrinter.IsOptionValueValid(AKeyword, AValue: pchar): boolean;
  460. var
  461. Option: pppd_option_t;
  462. i: Integer;
  463. begin
  464. result := false;
  465. if (fcupsPrinter=nil) or (fcupsppd=nil) then
  466. exit;
  467. Option := ppdFindOption(fcupsppd, AKeyword);
  468. if Option=nil then
  469. exit;
  470. i:=0;
  471. while i<Option^.num_choices do
  472. begin
  473. if strcomp(Option^.choices[i].choice, AValue)=0 then
  474. begin
  475. result := true;
  476. break;
  477. end;
  478. inc(i);
  479. end;
  480. end;
  481. function TCUPSPrinter.PPDOptionChoiceFrom(OptionStr, aKeyOrValue: string;
  482. IsKey:boolean): pppd_choice_t;
  483. var
  484. i: Integer;
  485. option: pppd_option_t;
  486. p: pchar;
  487. begin
  488. result := nil;
  489. if (fcupsPrinter=nil) or (fcupsppd=nil) then
  490. exit;
  491. option := ppdFindOption(fcupsppd, pchar(OptionStr));
  492. if option=nil then
  493. exit;
  494. for i:=0 to option^.num_choices-1 do
  495. begin
  496. if IsKey then
  497. p := @option^.choices[i].choice
  498. else
  499. p := @option^.choices[i].text;
  500. if strcomp(p, pchar(aKeyOrValue))=0 then
  501. begin
  502. result := @option^.choices[i];
  503. break;
  504. end;
  505. end;
  506. end;
  507. //Set State of Job
  508. procedure TCUPSPrinter.SetJobState(aJobId : LongInt; aOp : ipp_op_t);
  509. var Request,R : Pipp_t; //IPP Request
  510. Language : Pcups_lang_t; //Default Language
  511. URI : Array[0..HTTP_MAX_URI] of Char; //Printer URI
  512. begin
  513. if not CUPSLibInstalled then Exit;
  514. if (Printers.Count>0) then
  515. begin
  516. if Assigned(fcupsPrinter) then
  517. begin
  518. R:=nil;
  519. DoCupsConnect;
  520. Request:=ippNew();
  521. Language:=cupsLangDefault();
  522. ippAddString(Request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
  523. 'attributes-charset', '', cupsLangEncoding(language));
  524. ippAddString(Request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
  525. 'attributes-natural-language', '', Language^.language);
  526. URI:=Format('http://%s:%d/jobs/%d',[cupsServer,ippPort,aJobId]);
  527. ippAddString(Request,IPP_TAG_OPERATION,IPP_TAG_URI,'job-uri','',URI);
  528. ippAddString(Request,IPP_TAG_OPERATION,IPP_TAG_NAME,'requesting-user-name','',cupsUser());
  529. Request^.request.op.operation_id := aOp;
  530. Request^.request.op.request_id := 1;
  531. //Do the request and get back a response...
  532. R:=cupsDoRequest(fcupsHttp, Request, '/jobs/');
  533. if Assigned(R) then
  534. begin
  535. if (R^.request.status.status_code>IPP_OK_CONFLICT) then
  536. ippDelete(R);
  537. end;
  538. end;
  539. end;
  540. end;
  541. function TCUPSPrinter.GetCupsRequest : Pipp_t;
  542. var Request : Pipp_t; //IPP Request
  543. Language : Pcups_lang_t; //Default Language
  544. URI : Array[0..HTTP_MAX_URI] of Char; //Printer URI
  545. begin
  546. Result:=Nil;
  547. if not CUPSLibInstalled then Exit;
  548. if (Printers.Count>0) then
  549. begin
  550. if Assigned(fcupsPrinter) then
  551. begin
  552. DoCupsConnect;
  553. Request:=ippNew();
  554. {Build an IPP_GET_PRINTER_ATTRIBUTES request,
  555. which requires the following attributes:
  556. attributes-charset
  557. attributes-natural-language
  558. printer-uri}
  559. Request^.request.op.operation_id := IPP_GET_PRINTER_ATTRIBUTES;
  560. Request^.request.op.request_id := 1;
  561. Language:=cupsLangDefault;
  562. ippAddString(Request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
  563. 'attributes-charset', '', cupsLangEncoding(language));
  564. ippAddString(Request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
  565. 'attributes-natural-language', '', Language^.language);
  566. // or this syntax >>
  567. //URI:=Format('http://%s:%d/printers/%s',[cupsServer,ippPort,fcupsPrinter^.name]);
  568. URI:=Format('ipp://localhost/printers/%s',[fcupsPrinter^.name]);
  569. ippAddString(Request,IPP_TAG_OPERATION,IPP_TAG_URI,'printer-uri','',URI);
  570. //Do the request and get back a response...
  571. Result:=cupsDoRequest(fcupsHttp, Request, '/');
  572. if Assigned(Result) then
  573. begin
  574. if (Result^.request.status.status_code>IPP_OK_CONFLICT) then
  575. begin
  576. ippDelete(Result);
  577. Result:=nil;
  578. end;
  579. end;
  580. end;
  581. end;
  582. end;
  583. //Initialize the options with the default options of selected printer
  584. procedure TCUPSPrinter.SetOptionsOfPrinter;
  585. Var Opts : Pcups_option_t;
  586. Opt : Pcups_option_t;
  587. i : Integer;
  588. begin
  589. //if not CUPSLibInstalled then
  590. Exit;
  591. if (Printers.Count>0) then
  592. begin
  593. if Assigned(fcupsPrinter) then
  594. begin
  595. Opts := fcupsPrinter^.Options;
  596. for i:=0 to fcupsPrinter^.num_options-1 do
  597. begin
  598. Opt:=@Opts[i];
  599. cupsAddOption(Opt^.Name,Opt^.Value);
  600. end;
  601. end;
  602. end;
  603. end;
  604. //Enum all options associed with aKeyWord
  605. function TCUPSPrinter.EnumPPDChoice(Lst : TStrings;
  606. const aKeyWord : string; OptNames: TStrings = nil) : Integer;
  607. var i : integer;
  608. Option : Pppd_option_t;
  609. Choice : Pppd_choice_t;
  610. begin
  611. Result:=-1;
  612. if not CUPSLibInstalled then Exit;
  613. if not Assigned(Lst) then Exit;
  614. Lst.Clear;
  615. if (Printers.Count>0) then
  616. begin
  617. if Assigned(fcupsPrinter) then
  618. begin
  619. if Assigned(fcupsPPD) then
  620. begin
  621. Option:=nil;
  622. Option:=ppdFindOption(fcupsPPD,PChar(aKeyWord));
  623. If Assigned(Option) then
  624. begin
  625. for i:=0 to Option^.num_choices-1 do
  626. begin
  627. Choice:=@Option^.choices[i];
  628. if Choice^.marked=#1 then
  629. Result:=i;
  630. Lst.Add(Choice^.text);
  631. if Assigned(OptNames) then
  632. OptNames.Add(Choice^.choice);
  633. end;
  634. //Not marked choice then the choice is default
  635. if (Result<0) and (Lst.Count>0) then begin
  636. Result:=Lst.IndexOf(OPtion^.defchoice);
  637. if (Result<0)and Assigned(OptNames) then
  638. Result := OptNames.IndexOf(Option^.DefChoice);
  639. end;
  640. end;
  641. end;
  642. end;
  643. end;
  644. end;
  645. function TCUPSPrinter.GetPPDAttribute(const aName: string): string;
  646. var
  647. i : integer;
  648. AttrRoot : PPppd_attr_t;
  649. Attr : Pppd_attr_t;
  650. begin
  651. Result:='';
  652. if not CUPSLibInstalled then
  653. Exit;
  654. if (Printers.Count>0) and (fcupsPrinter<>nil) and (fcupsPPD<>nil) then
  655. begin
  656. i := 0;
  657. AttrRoot := fCupsPPD^.attrs;
  658. while (AttrRoot<>nil) and (i<fcupsPPD^.num_attrs) do
  659. begin
  660. Attr := AttrRoot^;
  661. if attr<>nil then
  662. begin
  663. if (StrComp(pchar(AName), Attr^.name)=0) then
  664. begin
  665. result := attr^.value;
  666. break;
  667. end;
  668. end;
  669. inc(i);
  670. inc(AttrRoot);
  671. end;
  672. end;
  673. end;
  674. procedure TCUPSPrinter.GetEnumAttributeString(aName: PChar; Lst: TStrings);
  675. var
  676. Reponse : Pipp_t; //IPP Reponse
  677. Attribute : Pipp_attribute_t; //Current attribute
  678. i : Integer;
  679. begin
  680. if not assigned(Lst) then
  681. raise Exception.Create('Lst must be assigned');
  682. if not CUPSLibInstalled then begin
  683. DebugLn(['TCUPSPrinter.GetEnumAttributeString CUPSLibInstalled not installed']);
  684. Exit;
  685. end;
  686. Reponse:=GetCupsRequest;
  687. if not Assigned(Reponse) then begin
  688. DebugLn(['TCUPSPrinter.GetEnumAttributeString no Response']);
  689. end else begin
  690. try
  691. Attribute:=ippFindAttribute(Reponse,aName, IPP_TAG_ZERO);
  692. if Assigned(Attribute) then begin
  693. for i:=0 to Attribute^.num_values-1 do
  694. begin
  695. if Attribute^.value_tag=IPP_TAG_INTEGER then
  696. Lst.add(IntToStr(Pipp_value_t(@Attribute^.values)[i].aInteger))
  697. else
  698. Lst.add(Pipp_value_t(@Attribute^.values)[i]._string.text);
  699. end;
  700. end else begin
  701. DebugLn(['TCUPSPrinter.GetEnumAttributeString Attribute not found: ',aName]);
  702. end;
  703. finally
  704. ippDelete(Reponse);
  705. end;
  706. end;
  707. end;
  708. function TCUPSPrinter.GetAttributeInteger(aName: PChar; DefaultValue : Integer): Integer;
  709. var
  710. Reponse : Pipp_t; //IPP Reponse
  711. Attribute : Pipp_attribute_t; //Current attribute
  712. begin
  713. Result:=DefaultValue;
  714. if not CUPSLibInstalled then Exit;
  715. Reponse:=GetCupsRequest;
  716. if Assigned(Reponse) then
  717. begin
  718. try
  719. Attribute:=ippFindAttribute(Reponse,aName, IPP_TAG_ZERO);
  720. if Assigned(Attribute) then
  721. Result:=Attribute^.values[0].aInteger;
  722. finally
  723. ippDelete(Reponse);
  724. end;
  725. end;
  726. end;
  727. function TCUPSPrinter.GetAttributeString(aName: PChar;
  728. const DefaultValue : string): string;
  729. var
  730. Reponse : Pipp_t; //IPP Reponse
  731. Attribute : Pipp_attribute_t; //Current attribute
  732. begin
  733. Result:=DefaultValue;
  734. if not CUPSLibInstalled then Exit;
  735. Reponse:=GetCupsRequest;
  736. if Assigned(Reponse) then
  737. begin
  738. try
  739. Attribute:=ippFindAttribute(Reponse,aName, IPP_TAG_ZERO);
  740. if Assigned(Attribute) then
  741. Result:=Attribute^.values[0]._string.text
  742. else begin
  743. DebugLn(['TCUPSPrinter.GetAttributeString failed: aName="',aName,'"']);
  744. end;
  745. finally
  746. ippDelete(Reponse);
  747. end;
  748. end;
  749. end;
  750. function TCUPSPrinter.GetAttributeBoolean(aName: PChar;
  751. DefaultValue : Boolean): Boolean;
  752. var
  753. Reponse : Pipp_t; //IPP Reponse
  754. Attribute : Pipp_attribute_t; //Current attribute
  755. begin
  756. Result:=DefaultValue;
  757. if not CUPSLibInstalled then Exit;
  758. Reponse:=GetCupsRequest;
  759. if Assigned(Reponse) then
  760. begin
  761. try
  762. Attribute:=ippFindAttribute(Reponse,aName, IPP_TAG_ZERO);
  763. if Assigned(Attribute) then
  764. Result:=(Attribute^.values[0].aBoolean=#1);
  765. finally
  766. ippDelete(Reponse);
  767. end;
  768. end;
  769. end;
  770. //Override this methode for assign an
  771. //file name at Canvas
  772. procedure TCUPSPrinter.DoBeginDoc;
  773. var
  774. NewPath: String;
  775. fs: TFileStream;
  776. function TryTemporaryPath(const Path: string): Boolean;
  777. var
  778. CurPath: String;
  779. begin
  780. Result:=false;
  781. CurPath:=CleanAndExpandDirectory(Path);
  782. if CurPath='' then exit(false);
  783. if not DirectoryIsWritable(CurPath) then exit;
  784. NewPath:=CurPath;
  785. Result:=true;
  786. end;
  787. begin
  788. if FBeginDocCount>0 then
  789. raise Exception.Create('TCUPSPrinter.DoBeginDoc already called. Maybe you forgot an EndDoc?');
  790. inherited DoBeginDoc;
  791. inc(FBeginDocCount);
  792. if (not TryTemporaryPath('~/tmp/'))
  793. and (not TryTemporaryPath('/tmp/'))
  794. and (not TryTemporaryPath('/var/tmp/')) then
  795. NewPath:='';
  796. FOutputFileName := AppendPathDelim(NewPath)+
  797. 'OutPrinter_'+FormatDateTime('yyyymmmddd-hhnnss',Now);
  798. if RawMode then
  799. FOutputFileName := FOutputFileName + '.raw'
  800. else begin
  801. FOutputFileName := FOutputFileName + '.ps';
  802. TFilePrinterCanvas(Canvas).OutputFileName := FOutputFileName;
  803. end;
  804. // test writing, on error this raises exception showing the user the filename
  805. fs:=TFileStream.Create(FOutputFilename,fmCreate);
  806. try
  807. fs.Write(FOutputFilename[1],1);
  808. finally
  809. fs.free;
  810. end;
  811. DeleteFileUTF8(FOutputFilename);
  812. end;
  813. //If not aborted, send PostScript file to printer.
  814. //After, delete this file.
  815. procedure TCUPSPrinter.DoEndDoc(aAborted: Boolean);
  816. var
  817. CupsResult: LongInt;
  818. begin
  819. inherited DoEndDoc(aAborted);
  820. dec(FBeginDocCount);
  821. Exclude(FStates,cpsPaperRectValid);
  822. if RawMode then begin
  823. if not aAborted and (FRawModeStream<>nil)
  824. and (FRawModeStream.Size>0) then
  825. begin
  826. try
  827. FRawModeStream.SaveToFile(FOutputFileName);
  828. finally
  829. FRawModeStream.Clear;
  830. end;
  831. end;
  832. end else
  833. TFilePrinterCanvas(Canvas).OutputFileName:='';
  834. if not aAborted then begin
  835. if not FileExistsUTF8(FOutputFileName) then
  836. raise Exception.Create('Unable to write to "'+FOutputFileName+'"');
  837. {$IFDEF LogPrintoutFile}
  838. CopyFile(FOutputFileName, 'printjob'+ExtractFileExt(FOutputFileName));
  839. {$ENDIF}
  840. try
  841. {$IFNDEF DoNotPrint}
  842. if Filename<>'' then
  843. CopyFile(FOutputFileName, FileName)
  844. else begin
  845. CupsResult:=PrintFile(FOutputFileName);
  846. if CupsResult<=0 then
  847. raise Exception.Create('CUPS printing: '+GetLastError);
  848. end;
  849. {$ENDIF}
  850. finally
  851. DeleteFileUTF8(FOutputFilename);
  852. end;
  853. end;
  854. end;
  855. procedure TCUPSPrinter.DoResetPrintersList;
  856. begin
  857. if Assigned(fcupsPPD) then
  858. begin
  859. ppdClose(fcupsPPD);
  860. fcupsPPD:=nil;
  861. end;
  862. if fcupsPPDName<>'' then
  863. begin
  864. DeleteFileUTF8(fcupsPPDName);
  865. fcupsPPDName:='';
  866. end;
  867. FreeOptions;
  868. if Assigned(fcupsPrinters) and CUPSLibInstalled then begin
  869. cupsFreeDests(Printers.Count,fcupsPrinters);
  870. fCupsPrinter := nil;
  871. end;
  872. inherited DoResetPrintersList;
  873. end;
  874. procedure TCUPSPrinter.DoEnumPrinters(Lst: TStrings);
  875. Var i,Num : Integer;
  876. P : Pcups_dest_t;
  877. begin
  878. inherited DoEnumPrinters(Lst);
  879. {$IFDEF NOPRINTERS}
  880. Lst.Clear;
  881. Exit;
  882. {$ENDIF}
  883. if not CUPSLibInstalled then Exit;
  884. Num:=cupsGetDests(@fcupsPrinters);
  885. For i:=0 to Num-1 do
  886. begin
  887. P:=nil;
  888. P:=@fcupsPrinters[i];
  889. if Assigned(P) then
  890. begin
  891. if P^.is_default<>0 then
  892. Lst.Insert(0,P^.name)
  893. else
  894. Lst.Add(P^.name);
  895. end;
  896. end;
  897. end;
  898. procedure TCUPSPrinter.DoEnumPapers(Lst: TStrings);
  899. var
  900. choice: Pppd_choice_t;
  901. Option: Pppd_option_t;
  902. c: Integer;
  903. begin
  904. //DebugLn(['TCUPSPrinter.DoEnumPapers ',dbgsName(Self)]);
  905. //TODO: note that we are returning here the list of paper "keys"
  906. // not the human readable paper names. Modify cups support
  907. // to return human readable paper names.
  908. Lst.Clear;
  909. FCupsDefaultPaper := '';
  910. if CupsPPD<>nil then
  911. begin
  912. Option := ppdFindOption(CupsPPD, PChar('PageSize'));
  913. Choice := Option^.choices;
  914. fCupsDefaultPaper := Option^.defchoice;
  915. c := 0;
  916. while (Choice<>nil) and (c<Option^.num_choices) do
  917. begin
  918. lst.AddObject(Choice^.Choice, TObject(Choice));
  919. inc(choice);
  920. inc(c);
  921. end;
  922. end;
  923. fCupsPapersCount := lst.Count;
  924. end;
  925. function TCUPSPrinter.DoSetPrinter(aName: string): Integer;
  926. Var i : Integer;
  927. P : Pcups_dest_t;
  928. Fn : String;
  929. begin
  930. //debugln('TCUPSPrinter.DoSetPrinter aName="',aName,'"');
  931. Result:=inherited DoSetPrinter(aName);
  932. if not CUPSLibInstalled then Exit;
  933. //debugln('TCUPSPrinter.DoSetPrinter B Printers.Count=',dbgs(Printers.Count));
  934. //Set the current printer. If aName='' then use a default Printer (index 0)
  935. If (Printers.Count>0) then
  936. begin
  937. if (aName<>'') and Assigned(fcupsPPD) then
  938. begin
  939. //Printer changed ?
  940. i:=Printers.IndexOf(aName);
  941. if i=PrinterIndex then
  942. begin
  943. Result:=PrinterIndex;
  944. //debugln('TCUPSPrinter.DoSetPrinter no change');
  945. Exit;
  946. end;
  947. end;
  948. //Clear all existing options
  949. FreeOptions;
  950. if Assigned(fcupsPPD) then
  951. begin
  952. ppdClose(fcupsPPD);
  953. fcupsPPD:=nil;
  954. if fcupsPPDName<>'' then
  955. begin
  956. DeleteFileUTF8(fcupsPPDName);
  957. fcupsPPDName:='';
  958. end;
  959. end;
  960. if aName='' then
  961. i:=0
  962. else
  963. i:=Printers.IndexOf(aName);
  964. if i>-1 then
  965. begin
  966. Result:=i;
  967. P:=nil;
  968. P:=cupsGetDest(PChar(aName),nil,Printers.Count,fcupsPrinters);
  969. if not Assigned(P) then
  970. raise Exception.Create(Format('"%s" is not a valid printer.',[aName]));
  971. fcupsPrinter:=P;
  972. //Open linked ppdfile
  973. Fn:=cupsGetPPD(PChar(aName));
  974. fcupsPPD:=ppdOpenFile(PChar(Fn));
  975. fcupsPPDName:=Fn;
  976. {$IFDEF DebugCUPS}
  977. DebugPPD;
  978. DebugCapabilities;
  979. {$ENDIF}
  980. end;
  981. end
  982. else
  983. begin
  984. PrinterIndex:=-1;
  985. fcupsPPD:=nil;
  986. end;
  987. end;
  988. function TCUPSPrinter.DoGetCopies: Integer;
  989. begin
  990. if not (cpsCopiesValid in FStates) then begin
  991. fCachedCopies:=inherited DoGetCopies;
  992. //Get default value if defined
  993. fCachedCopies:=GetAttributeInteger('copies-default',fCachedCopies);
  994. //Get Copies in options or return default value
  995. fCachedCopies:=StrToIntdef(cupsGetOption('copies'),fCachedCopies);
  996. {$IFDEF UseCache}
  997. Include(FStates,cpsCopiesValid);
  998. {$ENDIF}
  999. end;
  1000. Result:=fCachedCopies;
  1001. end;
  1002. procedure TCUPSPrinter.DoSetCopies(aValue: Integer);
  1003. var i : Integer;
  1004. begin
  1005. {$IFDEF UseCache}
  1006. if aValue=DoGetCopies then exit;
  1007. Exclude(FStates,cpsCopiesValid);
  1008. {$ENDIF}
  1009. inherited DoSetCopies(aValue);
  1010. if Printers.Count>0 then
  1011. begin
  1012. if not Assigned(fcupsOptions) then
  1013. SetOptionsOfPrinter;
  1014. i:=aValue;
  1015. if i<1 then i:=1;
  1016. cupsAddOption('copies',IntToStr(i));
  1017. end;
  1018. end;
  1019. function TCUPSPrinter.DoGetOrientation: TPrinterOrientation;
  1020. var i : Integer;
  1021. begin
  1022. if not (cpsOrientationValid in FStates) then begin
  1023. if Printers.Count>0 then
  1024. begin
  1025. //Default orientation value
  1026. i:=GetAttributeInteger('orientation-requested-default',3);
  1027. // check if rotation is automatic or out-of-range
  1028. if not (i in [3,4,5,6]) then
  1029. i:=3; // yep, then for us this means portait
  1030. fCachedOrientation:=TPrinterOrientation(i-3);
  1031. end;
  1032. Include(FStates,cpsOrientationValid);
  1033. end;
  1034. Result:=fCachedOrientation;
  1035. {$IFDEF DebugCUPS}
  1036. DebugLn('DoGetOrientation: result=%d',[ord(Result)]);
  1037. {$ENDIF}
  1038. end;
  1039. procedure TCUPSPrinter.DoSetOrientation(aValue: TPrinterOrientation);
  1040. begin
  1041. if aValue=DoGetOrientation then
  1042. exit;
  1043. Exclude(FStates,cpsPaperRectValid);
  1044. inherited DoSetOrientation(aValue);
  1045. fcachedOrientation := AValue;
  1046. Include(FStates,cpsOrientationValid);
  1047. end;
  1048. function TCUPSPrinter.DoGetDefaultPaperName: string;
  1049. begin
  1050. if not (cpsDefaultPaperNameValid in FStates) then begin
  1051. fCachedGetDefaultPaperName:='';
  1052. if not CupsPapersListValid then
  1053. FCachedGetDefaultPaperName:=PaperSize.DefaultPaperName
  1054. else begin
  1055. if FCupsDefaultPaper<>'' then
  1056. fCachedGetDefaultPaperName:= FCupsDefaultPaper
  1057. else
  1058. fCachedGetDefaultPaperName:=
  1059. GetAttributeString('media-default',fCachedGetDefaultPaperName);
  1060. {$IFDEF UseCache}
  1061. Include(FStates,cpsDefaultPaperNameValid);
  1062. {$ENDIF}
  1063. end;
  1064. end;
  1065. Result:=fCachedGetDefaultPaperName;
  1066. end;
  1067. function TCUPSPrinter.DoGetPaperName: string;
  1068. begin
  1069. if not (cpsPaperNameValid in FStates) then begin
  1070. // paper is not yet retrieved for first time
  1071. // first try to see if there is a list of papers available
  1072. if not CupsPapersListValid then
  1073. FCachedPaperName := PaperSize.PaperName
  1074. else begin
  1075. fCachedPaperName := cupsGetOption('PageSize');
  1076. {$IFDEF UseCache}
  1077. Include(FStates,cpsPaperNameValid);
  1078. {$ENDIF}
  1079. end;
  1080. end;
  1081. Result:=fCachedPaperName;
  1082. end;
  1083. procedure TCUPSPrinter.DoSetPaperName(aName: string);
  1084. begin
  1085. {$IFDEF UseCache}
  1086. if aName=DoGetPaperName then exit;
  1087. Exclude(FStates,cpsPaperNameValid);
  1088. {$ENDIF}
  1089. inherited DoSetPaperName(aName);
  1090. if FCupsPapersCount<=0 then
  1091. PaperSize.PaperName:=AName
  1092. else
  1093. cupsAddOption('PageSize',aName)
  1094. end;
  1095. //Initialise aPaperRc with the aName paper rect
  1096. //Result : -1 no result
  1097. // 0 aPaperRc.WorkRect is a margins
  1098. // 1 aPaperRc.WorkRect is really the work rect
  1099. function TCUPSPrinter.DoGetPaperRect(aName: string;
  1100. var aPaperRc: TPaperRect): Integer;
  1101. var
  1102. P : Pppd_size_t;
  1103. Ky,Kx: Double;
  1104. begin
  1105. if (not (cpsPaperRectValid in FStates)) or
  1106. (fCachePaperRectName<>aName) then
  1107. begin
  1108. fCachePaperRectName:=aName;
  1109. FillChar(fCachePaperRect,SizeOf(fCachePaperRect),0);
  1110. fCachePaperRectResult:=inherited DoGetPaperRect(aName, aPaperRc);
  1111. {$IFDEF UseCache}
  1112. Include(FStates,cpsPaperRectValid);
  1113. {$ENDIF}
  1114. P:=nil;
  1115. if CUPSLibInstalled and Assigned(fcupsPPD) then
  1116. begin
  1117. P:=ppdPageSize(fcupsPPD,PChar(aName));
  1118. if Assigned(P) then
  1119. begin
  1120. fCachePaperRectResult:=1; //CUPS return margins
  1121. // Margins.
  1122. //
  1123. // Cups gives dimensions based on postcript language
  1124. // user space coordinates system which is something like
  1125. //
  1126. // +y +--> +x
  1127. // ^ but our system is |
  1128. // | v
  1129. // +--> +x +y
  1130. //
  1131. // so values in x are the same, but we need to invert values in y,
  1132. // the given bottom value is the margin size at the bottom, we need
  1133. // to re-calc. our bottom offset, and the given top value is offset
  1134. // top value of imageable area, we need to re-calc. our top offset,
  1135. // which is the margin size at the top of the page.
  1136. //
  1137. // The current implementation assumes that paper is fed short-edge-first
  1138. // either in portrait orientation, or in landscape orientation.
  1139. //
  1140. // In landscape orientation, printable margins should preserved.
  1141. // It's based on a 90 degree counterclock wise paper rotation
  1142. //
  1143. // FEED DIRECTION FEED DIRECTION
  1144. //
  1145. // /\ /\
  1146. // / \ / \
  1147. // || ||
  1148. // || ||
  1149. //
  1150. // PORTRAIT LANDSCAPE
  1151. // +-----------------+ +-----------------+
  1152. // | t | | t |
  1153. // | +---------+ | | +---------+ |
  1154. // | | ( ) | | | | | / | |
  1155. // | l | --+-- | r | | l |()-+--- | r |
  1156. // | | / \ | | | | | \ | |
  1157. // | +---------+ | | +---------+ |
  1158. // | b | | b |
  1159. // +-----------------+ +-----------------+
  1160. //
  1161. // REVERSE PORTRAIT REVERSE LANDSCAPE
  1162. // +-----------------+ +-----------------+
  1163. // | t | | t |
  1164. // | +---------+ | | +---------+ |
  1165. // | | \ / | | | | \ | | |
  1166. // | l | --+-- | r | | l | ---+-()| r |
  1167. // | | ( ) | | | | / | | |
  1168. // | +---------+ | | +---------+ |
  1169. // | b | | b |
  1170. // +-----------------+ +-----------------+
  1171. //
  1172. Kx := Printer.XDPI/72;
  1173. Ky := Printer.YDPI/72;
  1174. if Orientation in [poPortrait, poReversePortrait] then begin
  1175. fCachePaperRect.PhysicalRect.Right:=Round(P^.Width*Kx);
  1176. fCachePaperRect.PhysicalRect.Bottom:=Round(P^.Length*Ky);
  1177. fCachePaperRect.WorkRect.Left:=Round(P^.Left*Kx);
  1178. fCachePaperRect.WorkRect.Right:=Round(P^.Right*Kx);
  1179. fCachePaperRect.WorkRect.Top:=Round((P^.Length-P^.Top)*Ky);
  1180. fCachePaperRect.WorkRect.Bottom:=Round((P^.Length-P^.Bottom)*Ky);
  1181. end else begin
  1182. FCachePaperRect.PhysicalRect.Right:=Round(P^.Length*Kx);
  1183. FCachePaperRect.PhysicalRect.Bottom:=Round(P^.Width*Ky);
  1184. FCachePaperRect.WorkRect.Left:=Round((P^.Length-P^.Top)*Kx);
  1185. FCachePaperRect.WorkRect.Right:=Round((P^.Length-P^.Bottom)*Kx);
  1186. FCachePaperRect.WorkRect.Top:=Round((P^.Width-P^.Right)*Ky);
  1187. FCachePaperRect.WorkRect.Bottom:=Round((p^.width - P^.left)*Ky);
  1188. end;
  1189. {$IFDEF DebugCUPS}
  1190. with P^ do
  1191. DebugLn('ORG: Width=%f Length=%f Left=%f Right=%f Top=%f Bottom=%f Name=%s',
  1192. [Width,Length,Left,Right,Top,Bottom,string(Name)]);
  1193. with FCachePaperRect do
  1194. DebugLn('NEW: Width=%d Length=%d Left=%d Top=%d Right=%d Bottom=%d ml=%d mt=%d mr=%d mb=%d',
  1195. [PhysicalRect.Right,PhysicalRect.Bottom,WorkRect.Left,WorkRect.Top,WorkRect.Right,WorkRect.Bottom,
  1196. WorkRect.Left,WorkRect.Top,PhysicalRect.Right-WorkRect.Right,
  1197. PhysicalRect.Bottom-WorkRect.Bottom]);
  1198. {$ENDIF}
  1199. end;
  1200. end;
  1201. if P=nil then begin
  1202. FCachePaperRect := PaperSize.PaperRectOf[AName];
  1203. fCachePaperRectResult:=1
  1204. end;
  1205. end;
  1206. Result:=fCachePaperRectResult;
  1207. aPaperRc:=fCachePaperRect;
  1208. end;
  1209. function TCUPSPrinter.DoGetPrinterState: TPrinterState;
  1210. var //Request : Pipp_t; //IPP Request
  1211. //Reponse : Pipp_t; //IPP Reponse
  1212. //Attribute : Pipp_attribute_t; //Current attribute
  1213. //Language : Pcups_lang_t; //Default Language
  1214. aState : ipp_pstate_t; //Printer state
  1215. //URI : Array[0..HTTP_MAX_URI] of Char; //Printer URI
  1216. begin
  1217. Result:=inherited DoGetPrinterState;
  1218. aState:=ipp_pstate_t(GetAttributeInteger('printer-state',0));
  1219. Case aState of
  1220. IPP_PRINTER_IDLE : Result:=psReady;
  1221. IPP_PRINTER_PROCESSING : Result:=psPrinting;
  1222. IPP_PRINTER_STOPPED : Result:=psStopped;
  1223. end;
  1224. end;
  1225. function TCUPSPrinter.DoGetDefaultCanvasClass: TPrinterCanvasRef;
  1226. begin
  1227. {$IFDEF UseCairo}
  1228. Result := TCairoPsCanvas;
  1229. {$ELSE}
  1230. Result := TPostscriptPrinterCanvas;
  1231. {$ENDIF}
  1232. end;
  1233. function TCUPSPrinter.GetPrinterType: TPrinterType;
  1234. Var i : Integer;
  1235. begin
  1236. Result:=inherited GetPrinterType;
  1237. i:=GetAttributeInteger('printer-type',CUPS_PRINTER_LOCAL);
  1238. If (i and CUPS_PRINTER_REMOTE)=CUPS_PRINTER_REMOTE then
  1239. Result:=ptNetWork;
  1240. end;
  1241. function TCUPSPrinter.GetCanPrint: Boolean;
  1242. begin
  1243. Result:=inherited GetCanPrint;
  1244. Result:=GetAttributeBoolean('printer-is-accepting-jobs',Result)
  1245. end;
  1246. initialization
  1247. if Assigned(Printer) then
  1248. Printer.Free;
  1249. Printer:=TCUPSPrinter.Create;
  1250. FINALIZATION
  1251. // Free the printer before unloading library
  1252. Printer.Free;
  1253. Printer:=nil;
  1254. //Unload CUPSLib if loaded
  1255. FinalizeCups;
  1256. END.