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