PageRenderTime 177ms CodeModel.GetById 52ms app.highlight 115ms RepoModel.GetById 1ms app.codeStats 0ms

/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{**************************************************************
   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; var 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.DoResetPrintersList;
 951begin
 952  if Assigned(fcupsPPD) then
 953  begin
 954    ppdClose(fcupsPPD);
 955    fcupsPPD:=nil;
 956  end;
 957
 958  if fcupsPPDName<>'' then
 959  begin
 960    DeleteFileUTF8(fcupsPPDName);
 961    fcupsPPDName:='';
 962  end;
 963
 964  FreeOptions;
 965  if Assigned(fcupsPrinters) and CUPSLibInstalled then begin
 966    cupsFreeDests(Printers.Count,fcupsPrinters);
 967    fCupsPrinter := nil;
 968  end;
 969
 970  inherited DoResetPrintersList;
 971end;
 972
 973procedure TCUPSPrinter.DoEnumPrinters(Lst: TStrings);
 974Var i,Num   : Integer;
 975    P       : Pcups_dest_t;
 976begin
 977  inherited DoEnumPrinters(Lst);
 978  {$IFDEF NOPRINTERS}
 979  Lst.Clear;
 980  Exit;
 981  {$ENDIF}
 982  if not CUPSLibInstalled then Exit;
 983
 984  Num:=cupsGetDests(@fcupsPrinters);
 985  For i:=0 to Num-1 do
 986  begin
 987    P:=nil;
 988    P:=@fcupsPrinters[i];
 989    if Assigned(P) then
 990    begin
 991      if P^.is_default<>0 then
 992        Lst.Insert(0,P^.name)
 993      else
 994        Lst.Add(P^.name);
 995    end;
 996  end;
 997end;
 998
 999procedure TCUPSPrinter.DoEnumPapers(Lst: TStrings);
1000var
1001  choice: Pppd_choice_t;
1002  Option: Pppd_option_t;
1003  c: Integer;
1004begin
1005  //DebugLn(['TCUPSPrinter.DoEnumPapers ',dbgsName(Self)]);
1006
1007  //TODO: note that we are returning here the list of paper "keys"
1008  //      not the human readable paper names. Modify cups support
1009  //      to return human readable paper names.
1010
1011  Lst.Clear;
1012  FCupsDefaultPaper := '';
1013  if CupsPPD<>nil then
1014  begin
1015    Option := ppdFindOption(CupsPPD, PChar('PageSize'));
1016    Choice := Option^.choices;
1017    fCupsDefaultPaper := Option^.defchoice;
1018    c := 0;
1019    while (Choice<>nil) and (c<Option^.num_choices) do
1020    begin
1021      lst.AddObject(Choice^.Choice, TObject(Choice));
1022      inc(choice);
1023      inc(c);
1024    end;
1025  end;
1026
1027  fCupsPapersCount := lst.Count;
1028end;
1029
1030function TCUPSPrinter.DoSetPrinter(aName: string): Integer;
1031Var i  : Integer;
1032    P  : Pcups_dest_t;
1033    Fn : String;
1034begin
1035  //debugln('TCUPSPrinter.DoSetPrinter aName="',aName,'"');
1036  Result:=inherited DoSetPrinter(aName);
1037  if not CUPSLibInstalled then Exit;
1038  //debugln('TCUPSPrinter.DoSetPrinter B Printers.Count=',dbgs(Printers.Count));
1039
1040  //Set the current printer. If aName='' then use a default Printer (index 0)
1041  If (Printers.Count>0) then
1042  begin
1043    if (aName<>'') and Assigned(fcupsPPD) then
1044    begin
1045      //Printer changed ?
1046      i:=Printers.IndexOf(aName);
1047      if i=PrinterIndex then
1048      begin
1049        Result:=PrinterIndex;
1050        //debugln('TCUPSPrinter.DoSetPrinter no change');
1051        Exit;
1052      end;
1053    end;
1054
1055    //Clear all existing options
1056    FreeOptions;
1057
1058    if Assigned(fcupsPPD) then
1059    begin
1060      ppdClose(fcupsPPD);
1061      fcupsPPD:=nil;
1062
1063      if fcupsPPDName<>'' then
1064      begin
1065        DeleteFileUTF8(fcupsPPDName);
1066        fcupsPPDName:='';
1067      end;
1068    end;
1069
1070
1071    if aName='' then
1072      i:=0
1073    else
1074      i:=Printers.IndexOf(aName);
1075
1076    if i>-1 then
1077    begin
1078      Result:=i;
1079      
1080      P:=nil;
1081      P:=cupsGetDest(PChar(aName),nil,Printers.Count,fcupsPrinters);
1082      if not Assigned(P) then
1083        raise Exception.Create(Format('"%s" is not a valid printer.',[aName]));
1084      fcupsPrinter:=P;
1085
1086      //Open linked ppdfile
1087      Fn:=cupsGetPPD(PChar(aName));
1088      fcupsPPD:=ppdOpenFile(PChar(Fn));
1089      fcupsPPDName:=Fn;
1090      {$IFDEF DebugCUPS}
1091      DebugPPD;
1092      DebugCapabilities;
1093      {$ENDIF}
1094    end;
1095  end
1096  else
1097  begin
1098    PrinterIndex:=-1;
1099    fcupsPPD:=nil;
1100  end;
1101end;
1102
1103function TCUPSPrinter.DoGetCopies: Integer;
1104begin
1105  if not (cpsCopiesValid in FStates) then begin
1106    fCachedCopies:=inherited DoGetCopies;
1107
1108    //Get default value if defined
1109    fCachedCopies:=GetAttributeInteger('copies-default',fCachedCopies);
1110    //Get Copies in options or return default value
1111    fCachedCopies:=StrToIntdef(cupsGetOption('copies'),fCachedCopies);
1112    {$IFDEF UseCache}
1113    Include(FStates,cpsCopiesValid);
1114    {$ENDIF}
1115  end;
1116  Result:=fCachedCopies;
1117end;
1118
1119procedure TCUPSPrinter.DoSetCopies(aValue: Integer);
1120var i : Integer;
1121begin
1122  {$IFDEF UseCache}
1123  if aValue=DoGetCopies then exit;
1124  Exclude(FStates,cpsCopiesValid);
1125  {$ENDIF}
1126  inherited DoSetCopies(aValue);
1127
1128  if Printers.Count>0 then
1129  begin
1130    if not Assigned(fcupsOptions) then
1131      SetOptionsOfPrinter;
1132    i:=aValue;
1133    if i<1 then i:=1;
1134    cupsAddOption('copies',IntToStr(i));
1135  end;
1136end;
1137
1138function TCUPSPrinter.DoGetOrientation: TPrinterOrientation;
1139var i  : Integer;
1140begin
1141  if not (cpsOrientationValid in FStates) then begin
1142    if Printers.Count>0 then
1143    begin
1144      //Default orientation value
1145      i:=GetAttributeInteger('orientation-requested-default',3);
1146      // check if rotation is automatic or out-of-range
1147      if not (i in [3,4,5,6]) then
1148        i:=3; // yep, then for us this means portait
1149      fCachedOrientation:=TPrinterOrientation(i-3);
1150    end;
1151    Include(FStates,cpsOrientationValid);
1152  end;
1153  Result:=fCachedOrientation;
1154  {$IFDEF DebugCUPS}
1155  DebugLn('DoGetOrientation: result=%d',[ord(Result)]);
1156  {$ENDIF}
1157end;
1158
1159procedure TCUPSPrinter.DoSetOrientation(aValue: TPrinterOrientation);
1160begin
1161  if aValue=DoGetOrientation then
1162    exit;
1163  Exclude(FStates,cpsPaperRectValid);
1164  inherited DoSetOrientation(aValue);
1165  fcachedOrientation := AValue;
1166  Include(FStates,cpsOrientationValid);
1167end;
1168
1169function TCUPSPrinter.DoGetDefaultPaperName: string;
1170begin
1171  if not (cpsDefaultPaperNameValid in FStates) then begin
1172    fCachedGetDefaultPaperName:='';
1173    if not CupsPapersListValid then
1174      FCachedGetDefaultPaperName:=PaperSize.DefaultPaperName
1175    else begin
1176      if FCupsDefaultPaper<>'' then
1177        fCachedGetDefaultPaperName:= FCupsDefaultPaper
1178      else
1179        fCachedGetDefaultPaperName:=
1180                 GetAttributeString('media-default',fCachedGetDefaultPaperName);
1181      {$IFDEF UseCache}
1182      Include(FStates,cpsDefaultPaperNameValid);
1183      {$ENDIF}
1184    end;
1185  end;
1186  Result:=fCachedGetDefaultPaperName;
1187end;
1188
1189function TCUPSPrinter.DoGetPaperName: string;
1190begin
1191  if not (cpsPaperNameValid in FStates) then begin
1192    // paper is not yet retrieved for first time
1193    // first try to see if there is a list of papers available
1194    if not CupsPapersListValid then
1195      FCachedPaperName := PaperSize.PaperName
1196    else begin
1197      fCachedPaperName := cupsGetOption('PageSize');
1198      {$IFDEF UseCache}
1199      Include(FStates,cpsPaperNameValid);
1200      {$ENDIF}
1201    end;
1202  end;
1203  Result:=fCachedPaperName;
1204end;
1205
1206procedure TCUPSPrinter.DoSetPaperName(aName: string);
1207begin
1208  {$IFDEF UseCache}
1209  if aName=DoGetPaperName then exit;
1210  Exclude(FStates,cpsPaperNameValid);
1211  {$ENDIF}
1212  inherited DoSetPaperName(aName);
1213  
1214  if FCupsPapersCount<=0 then
1215    PaperSize.PaperName:=AName
1216  else
1217    cupsAddOption('PageSize',aName)
1218end;
1219
1220//Initialise aPaperRc with the aName paper rect
1221//Result : -1 no result
1222//          0 aPaperRc.WorkRect is a margins
1223//          1 aPaperRc.WorkRect is really the work rect
1224function TCUPSPrinter.DoGetPaperRect(aName: string;
1225  var aPaperRc: TPaperRect): Integer;
1226  
1227var
1228  P : Pppd_size_t;
1229  Ky,Kx: Double;
1230begin
1231  if (not (cpsPaperRectValid in FStates)) or
1232    (fCachePaperRectName<>aName) then
1233  begin
1234    fCachePaperRectName:=aName;
1235    FillChar(fCachePaperRect,SizeOf(fCachePaperRect),0);
1236    fCachePaperRectResult:=inherited DoGetPaperRect(aName, aPaperRc);
1237    {$IFDEF UseCache}
1238    Include(FStates,cpsPaperRectValid);
1239    {$ENDIF}
1240
1241    P:=nil;
1242    if CUPSLibInstalled and Assigned(fcupsPPD) then
1243    begin
1244      P:=ppdPageSize(fcupsPPD,PChar(aName));
1245      if Assigned(P) then
1246      begin
1247        fCachePaperRectResult:=1; //CUPS return margins
1248
1249        // Margins.
1250        //
1251        // Cups gives dimensions based on postcript language
1252        // user space coordinates system which is something like
1253        //
1254        //  +y                                 +--> +x
1255        //   ^           but our system is     |
1256        //   |                                 v
1257        //   +--> +x                           +y
1258        //
1259        //  so values in x are the same, but we need to invert values in y,
1260        //  the given bottom value is the margin size at the bottom, we need
1261        //  to re-calc. our bottom offset, and the given top value is offset
1262        //  top value of imageable area, we need to re-calc. our top offset,
1263        //  which is the margin size at the top of the page.
1264        //
1265        //  The current implementation assumes that paper is fed short-edge-first
1266        //  either in portrait orientation, or in landscape orientation.
1267        //
1268        //  In landscape orientation, printable margins should preserved.
1269        //  It's based on a 90 degree counterclock wise paper rotation
1270        //
1271        //     FEED DIRECTION             FEED DIRECTION
1272        //
1273        //           /\                         /\
1274        //          /  \                       /  \
1275        //           ||                         ||
1276        //           ||                         ||
1277        //
1278        //     PORTRAIT                   LANDSCAPE
1279        //     +-----------------+        +-----------------+
1280        //     |        t        |        |        t        |
1281        //     |   +---------+   |        |   +---------+   |
1282        //     |   |   ( )   |   |        |   |   |   / |   |
1283        //     | l |  --+--  | r |        | l |()-+---  | r |
1284        //     |   |   / \   |   |        |   |   |   \ |   |
1285        //     |   +---------+   |        |   +---------+   |
1286        //     |        b        |        |        b        |
1287        //     +-----------------+        +-----------------+
1288        //
1289        //     REVERSE PORTRAIT           REVERSE LANDSCAPE
1290        //     +-----------------+        +-----------------+
1291        //     |        t        |        |        t        |
1292        //     |   +---------+   |        |   +---------+   |
1293        //     |   |   \ /   |   |        |   | \   |   |   |
1294        //     | l |  --+--  | r |        | l |  ---+-()| r |
1295        //     |   |   ( )   |   |        |   | /   |   |   |
1296        //     |   +---------+   |        |   +---------+   |
1297        //     |        b        |        |        b        |
1298        //     +-----------------+        +-----------------+
1299        //
1300        Kx := Printer.XDPI/72;
1301        Ky := Printer.YDPI/72;
1302        if Orientation in [poPortrait, poReversePortrait] then begin
1303          fCachePaperRect.PhysicalRect.Right:=Round(P^.Width*Kx);
1304          fCachePaperRect.PhysicalRect.Bottom:=Round(P^.Length*Ky);
1305          fCachePaperRect.WorkRect.Left:=Round(P^.Left*Kx);
1306          fCachePaperRect.WorkRect.Right:=Round(P^.Right*Kx);
1307          fCachePaperRect.WorkRect.Top:=Round((P^.Length-P^.Top)*Ky);
1308          fCachePaperRect.WorkRect.Bottom:=Round((P^.Length-P^.Bottom)*Ky);
1309        end else begin
1310          FCachePaperRect.PhysicalRect.Right:=Round(P^.Length*Kx);
1311          FCachePaperRect.PhysicalRect.Bottom:=Round(P^.Width*Ky);
1312          FCachePaperRect.WorkRect.Left:=Round((P^.Length-P^.Top)*Kx);
1313          FCachePaperRect.WorkRect.Right:=Round((P^.Length-P^.Bottom)*Kx);
1314          FCachePaperRect.WorkRect.Top:=Round((P^.Width-P^.Right)*Ky);
1315          FCachePaperRect.WorkRect.Bottom:=Round((p^.width - P^.left)*Ky);
1316        end;
1317
1318        {$IFDEF DebugCUPS}
1319        with P^ do
1320        DebugLn('ORG: Width=%f Length=%f Left=%f Right=%f Top=%f Bottom=%f Name=%s',
1321          [Width,Length,Left,Right,Top,Bottom,string(Name)]);
1322        with FCachePaperRect do
1323        DebugLn('NEW: Width=%d Length=%d Left=%d Top=%d Right=%d Bottom=%d ml=%d mt=%d mr=%d mb=%d',
1324          [PhysicalRect.Right,PhysicalRect.Bottom,WorkRect.Left,WorkRect.Top,WorkRect.Right,WorkRect.Bottom,
1325           WorkRect.Left,WorkRect.Top,PhysicalRect.Right-WorkRect.Right,
1326           PhysicalRect.Bottom-WorkRect.Bottom]);
1327        {$ENDIF}
1328      end;
1329    end;
1330    
1331    if P=nil then begin
1332      FCachePaperRect := PaperSize.PaperRectOf[AName];
1333      fCachePaperRectResult:=1
1334    end;
1335    
1336  end;
1337  Result:=fCachePaperRectResult;
1338  aPaperRc:=fCachePaperRect;
1339end;
1340
1341
1342function TCUPSPrinter.DoGetPrinterState: TPrinterState;
1343var //Request   : Pipp_t; //IPP Request
1344    //Reponse   : Pipp_t; //IPP Reponse
1345    //Attribute : Pipp_attribute_t; //Current attribute
1346    //Language  : Pcups_lang_t;     //Default Language
1347    aState    : ipp_pstate_t;     //Printer state
1348    //URI       : Array[0..HTTP_MAX_URI] of Char; //Printer URI
1349begin
1350  Result:=inherited DoGetPrinterState;
1351
1352  aState:=ipp_pstate_t(GetAttributeInteger('printer-state',0));
1353  Case aState of
1354    IPP_PRINTER_IDLE       : Result:=psReady;
1355    IPP_PRINTER_PROCESSING : Result:=psPrinting;
1356    IPP_PRINTER_STOPPED    : Result:=psStopped;
1357  end;
1358end;
1359
1360function TCUPSPrinter.DoGetDefaultCanvasClass: TPrinterCanvasRef;
1361begin
1362  {$IFDEF UseCairo}
1363  Result := TCairoPsCanvas;
1364  {$ELSE}
1365  Result := TPostscriptPrinterCanvas;
1366  {$ENDIF}
1367end;
1368
1369function TCUPSPrinter.GetPrinterType: TPrinterType;
1370Var i : Integer;
1371begin
1372  Result:=inherited GetPrinterType;
1373  i:=GetAttributeInteger('printer-type',CUPS_PRINTER_LOCAL);
1374  If (i and CUPS_PRINTER_REMOTE)=CUPS_PRINTER_REMOTE then
1375    Result:=ptNetWork;
1376end;
1377
1378function TCUPSPrinter.GetCanPrint: Boolean;
1379begin
1380  Result:=inherited GetCanPrint;
1381  Result:=GetAttributeBoolean('printer-is-accepting-jobs',Result)
1382end;
1383
1384initialization
1385  if Assigned(Printer) then
1386    Printer.Free;
1387
1388  Printer:=TCUPSPrinter.Create;
1389
1390FINALIZATION
1391  // Free the printer before unloading library
1392  Printer.Free;
1393  Printer:=nil;
1394  //Unload CUPSLib if loaded
1395  FinalizeCups;
1396
1397END.
1398