summaryrefslogtreecommitdiff
path: root/cups/cupspm.md
blob: f271c89d365488cf5f21a2d4dda0ab566801936d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
---
title: CUPS Programming Manual
author: Michael R Sweet
copyright: Copyright © 2007-2020 by Apple Inc. All Rights Reserved.
version: 2.3.4
...

> Please [file issues on Github](https://github.com/apple/cups/issues) to
> provide feedback on this document.


# Introduction

CUPS provides the "cups" library to talk to the different parts of CUPS and with
Internet Printing Protocol (IPP) printers. The "cups" library functions are
accessed by including the `<cups/cups.h>` header.

CUPS is based on the Internet Printing Protocol ("IPP"), which allows clients
(applications) to communicate with a server (the scheduler, printers, etc.) to
get a list of destinations, send print jobs, and so forth.  You identify which
server you want to communicate with using a pointer to the opaque structure
`http_t`.  The `CUPS_HTTP_DEFAULT` constant can be used when you want to talk to
the CUPS scheduler.


## Guidelines

When writing software (other than printer drivers) that uses the "cups" library:

- Do not use undocumented or deprecated APIs,
- Do not rely on pre-configured printers,
- Do not assume that printers support specific features or formats, and
- Do not rely on implementation details (PPDs, etc.)

CUPS is designed to insulate users and developers from the implementation
details of printers and file formats.  The goal is to allow an application to
supply a print file in a standard format with the user intent ("print four
copies, two-sided on A4 media, and staple each copy") and have the printing
system manage the printer communication and format conversion needed.

Similarly, printer and job management applications can use standard query
operations to obtain the status information in a common, generic form and use
standard management operations to control the state of those printers and jobs.

> **Note:**
>
> CUPS printer drivers necessarily depend on specific file formats and certain
> implementation details of the CUPS software.  Please consult the Postscript
> and raster printer driver developer documentation on
> [CUPS.org](https://www.cups.org/documentation.html) for more information.


## Terms Used in This Document

A *Destination* is a printer or print queue that accepts print jobs.  A
*Print Job* is a collection of one or more documents that are processed by a
destination using options supplied when creating the job.  A *Document* is a
file (JPEG image, PDF file, etc.) suitable for printing.  An *Option* controls
some aspect of printing, such as the media used. *Media* is the sheets or roll
that is printed on.  An *Attribute* is an option encoded for an Internet
Printing Protocol (IPP) request.


## Compiling Programs That Use the CUPS API

The CUPS libraries can be used from any C, C++, or Objective C program.
The method of compiling against the libraries varies depending on the
operating system and installation of CUPS. The following sections show how
to compile a simple program (shown below) in two common environments.

The following simple program lists the available destinations:

    #include <stdio.h>
    #include <cups/cups.h>

    int print_dest(void *user_data, unsigned flags, cups_dest_t *dest)
    {
      if (dest->instance)
        printf("%s/%s\n", dest->name, dest->instance);
      else
        puts(dest->name);

      return (1);
    }

    int main(void)
    {
      cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, print_dest, NULL);

      return (0);
    }


### Compiling with Xcode

In Xcode, choose *New Project...* from the *File* menu (or press SHIFT+CMD+N),
then select the *Command Line Tool* under the macOS Application project type.
Click *Next* and enter a name for the project, for example "firstcups".  Click
*Next* and choose a project directory. The click *Next* to create the project.

In the project window, click on the *Build Phases* group and expand the
*Link Binary with Libraries* section. Click *+*, type "libcups" to show the
library, and then double-click on `libcups.tbd`.

Finally, click on the `main.c` file in the sidebar and copy the example program
to the file.  Build and run (CMD+R) to see the list of destinations.


### Compiling with GCC

From the command-line, create a file called `simple.c` using your favorite
editor, copy the example to this file, and save.  Then run the following command
to compile it with GCC and run it:

    gcc -o simple `cups-config --cflags` simple.c `cups-config --libs`
    ./simple

The `cups-config` command provides the compiler flags (`cups-config --cflags`)
and libraries (`cups-config --libs`) needed for the local system.


# Working with Destinations

Destinations, which in CUPS represent individual printers or classes
(collections or pools) of printers, are represented by the `cups_dest_t`
structure which includes the name \(`name`), instance \(`instance`, saved
options/settings), whether the destination is the default for the user
\(`is_default`), and the options and basic information associated with that
destination \(`num_options` and `options`).

Historically destinations have been manually maintained by the administrator of
a system or network, but CUPS also supports dynamic discovery of destinations on
the current network.


## Finding Available Destinations

The `cupsEnumDests` function finds all of the available destinations:

     int
     cupsEnumDests(unsigned flags, int msec, int *cancel,
                   cups_ptype_t type, cups_ptype_t mask,
                   cups_dest_cb_t cb, void *user_data)

The `flags` argument specifies enumeration options, which at present must be
`CUPS_DEST_FLAGS_NONE`.

The `msec` argument specifies the maximum amount of time that should be used for
enumeration in milliseconds - interactive applications should keep this value to
5000 or less when run on the main thread.

The `cancel` argument points to an integer variable that, when set to a non-zero
value, will cause enumeration to stop as soon as possible.  It can be `NULL` if
not needed.

The `type` and `mask` arguments are bitfields that allow the caller to filter
the destinations based on categories and/or capabilities.  The destination's
"printer-type" value is masked by the `mask` value and compared to the `type`
value when filtering.  For example, to only enumerate destinations that are
hosted on the local system, pass `CUPS_PRINTER_LOCAL` for the `type` argument
and `CUPS_PRINTER_DISCOVERED` for the `mask` argument.  The following constants
can be used for filtering:

- `CUPS_PRINTER_CLASS`: A collection of destinations.
- `CUPS_PRINTER_FAX`: A facsimile device.
- `CUPS_PRINTER_LOCAL`: A local printer or class.  This constant has the value 0
  (no bits set) and is only used for the `type` argument and is paired with the
  `CUPS_PRINTER_REMOTE` or `CUPS_PRINTER_DISCOVERED` constant passed in the
  `mask` argument.
- `CUPS_PRINTER_REMOTE`: A remote (shared) printer or class.
- `CUPS_PRINTER_DISCOVERED`: An available network printer or class.
- `CUPS_PRINTER_BW`: Can do B&W printing.
- `CUPS_PRINTER_COLOR`: Can do color printing.
- `CUPS_PRINTER_DUPLEX`: Can do two-sided printing.
- `CUPS_PRINTER_STAPLE`: Can staple output.
- `CUPS_PRINTER_COLLATE`: Can quickly collate copies.
- `CUPS_PRINTER_PUNCH`: Can punch output.
- `CUPS_PRINTER_COVER`: Can cover output.
- `CUPS_PRINTER_BIND`: Can bind output.
- `CUPS_PRINTER_SORT`: Can sort output (mailboxes, etc.)
- `CUPS_PRINTER_SMALL`: Can print on Letter/Legal/A4-size media.
- `CUPS_PRINTER_MEDIUM`: Can print on Tabloid/B/C/A3/A2-size media.
- `CUPS_PRINTER_LARGE`: Can print on D/E/A1/A0-size media.
- `CUPS_PRINTER_VARIABLE`: Can print on rolls and custom-size media.

The `cb` argument specifies a function to call for every destination that is
found:

    typedef int (*cups_dest_cb_t)(void *user_data,
                                  unsigned flags,
                                  cups_dest_t *dest);

The callback function receives a copy of the `user_data` argument along with a
bitfield \(`flags`) and the destination that was found.  The `flags` argument
can have any of the following constant (bit) values set:

- `CUPS_DEST_FLAGS_MORE`: There are more destinations coming.
- `CUPS_DEST_FLAGS_REMOVED`: The destination has gone away and should be removed
  from the list of destinations a user can select.
- `CUPS_DEST_FLAGS_ERROR`: An error occurred.  The reason for the error can be
  found by calling the `cupsLastError` and/or `cupsLastErrorString` functions.

The callback function returns 0 to stop enumeration or 1 to continue.

> **Note:**
>
> The callback function will likely be called multiple times for the
> same destination, so it is up to the caller to suppress any duplicate
> destinations.

The following example shows how to use `cupsEnumDests` to get a filtered array
of destinations:

    typedef struct
    {
      int num_dests;
      cups_dest_t *dests;
    } my_user_data_t;

    int
    my_dest_cb(my_user_data_t *user_data, unsigned flags,
               cups_dest_t *dest)
    {
      if (flags & CUPS_DEST_FLAGS_REMOVED)
      {
       /*
        * Remove destination from array...
        */

        user_data->num_dests =
            cupsRemoveDest(dest->name, dest->instance,
                           user_data->num_dests,
                           &(user_data->dests));
      }
      else
      {
       /*
        * Add destination to array...
        */

        user_data->num_dests =
            cupsCopyDest(dest, user_data->num_dests,
                         &(user_data->dests));
      }

      return (1);
    }

    int
    my_get_dests(cups_ptype_t type, cups_ptype_t mask,
                 cups_dest_t **dests)
    {
      my_user_data_t user_data = { 0, NULL };

      if (!cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, type,
                         mask, (cups_dest_cb_t)my_dest_cb,
                         &user_data))
      {
       /*
        * An error occurred, free all of the destinations and
        * return...
        */

        cupsFreeDests(user_data.num_dests, user_dasta.dests);

        *dests = NULL;

        return (0);
      }

     /*
      * Return the destination array...
      */

      *dests = user_data.dests;

      return (user_data.num_dests);
    }


## Basic Destination Information

The `num_options` and `options` members of the `cups_dest_t` structure provide
basic attributes about the destination in addition to the user default options
and values for that destination.  The following names are predefined for various
destination attributes:

- "auth-info-required": The type of authentication required for printing to this
  destination: "none", "username,password", "domain,username,password", or
  "negotiate" (Kerberos).
- "printer-info": The human-readable description of the destination such as "My
  Laser Printer".
- "printer-is-accepting-jobs": "true" if the destination is accepting new jobs,
  "false" otherwise.
- "printer-is-shared": "true" if the destination is being shared with other
  computers, "false" otherwise.
- "printer-location": The human-readable location of the destination such as
  "Lab 4".
- "printer-make-and-model": The human-readable make and model of the destination
  such as "ExampleCorp LaserPrinter 4000 Series".
- "printer-state": "3" if the destination is idle, "4" if the destination is
  printing a job, and "5" if the destination is stopped.
- "printer-state-change-time": The UNIX time when the destination entered the
  current state.
- "printer-state-reasons": Additional comma-delimited state keywords for the
  destination such as "media-tray-empty-error" and "toner-low-warning".
- "printer-type": The `cups_ptype_t` value associated with the destination.
- "printer-uri-supported": The URI associated with the destination; if not set,
  this destination was discovered but is not yet setup as a local printer.

Use the `cupsGetOption` function to retrieve the value.  For example, the
following code gets the make and model of a destination:

    const char *model = cupsGetOption("printer-make-and-model",
                                      dest->num_options,
                                      dest->options);


## Detailed Destination Information

Once a destination has been chosen, the `cupsCopyDestInfo` function can be used
to gather detailed information about the destination:

    cups_dinfo_t *
    cupsCopyDestInfo(http_t *http, cups_dest_t *dest);

The `http` argument specifies a connection to the CUPS scheduler and is
typically the constant `CUPS_HTTP_DEFAULT`.  The `dest` argument specifies the
destination to query.

The `cups_dinfo_t` structure that is returned contains a snapshot of the
supported options and their supported, ready, and default values.  It also can
report constraints between different options and values, and recommend changes
to resolve those constraints.


### Getting Supported Options and Values

The `cupsCheckDestSupported` function can be used to test whether a particular
option or option and value is supported:

    int
    cupsCheckDestSupported(http_t *http, cups_dest_t *dest,
                           cups_dinfo_t *info,
                           const char *option,
                           const char *value);

The `option` argument specifies the name of the option to check.  The following
constants can be used to check the various standard options:

- `CUPS_COPIES`: Controls the number of copies that are produced.
- `CUPS_FINISHINGS`: A comma-delimited list of integer constants that control
  the finishing processes that are applied to the job, including stapling,
  punching, and folding.
- `CUPS_MEDIA`: Controls the media size that is used, typically one of the
  following: `CUPS_MEDIA_3X5`, `CUPS_MEDIA_4X6`, `CUPS_MEDIA_5X7`,
  `CUPS_MEDIA_8X10`, `CUPS_MEDIA_A3`, `CUPS_MEDIA_A4`, `CUPS_MEDIA_A5`,
  `CUPS_MEDIA_A6`, `CUPS_MEDIA_ENV10`, `CUPS_MEDIA_ENVDL`, `CUPS_MEDIA_LEGAL`,
  `CUPS_MEDIA_LETTER`, `CUPS_MEDIA_PHOTO_L`, `CUPS_MEDIA_SUPERBA3`, or
  `CUPS_MEDIA_TABLOID`.
- `CUPS_MEDIA_SOURCE`: Controls where the media is pulled from, typically either
  `CUPS_MEDIA_SOURCE_AUTO` or `CUPS_MEDIA_SOURCE_MANUAL`.
- `CUPS_MEDIA_TYPE`: Controls the type of media that is used, typically one of
  the following: `CUPS_MEDIA_TYPE_AUTO`, `CUPS_MEDIA_TYPE_ENVELOPE`,
  `CUPS_MEDIA_TYPE_LABELS`, `CUPS_MEDIA_TYPE_LETTERHEAD`,
  `CUPS_MEDIA_TYPE_PHOTO`, `CUPS_MEDIA_TYPE_PHOTO_GLOSSY`,
  `CUPS_MEDIA_TYPE_PHOTO_MATTE`, `CUPS_MEDIA_TYPE_PLAIN`, or
  `CUPS_MEDIA_TYPE_TRANSPARENCY`.
- `CUPS_NUMBER_UP`: Controls the number of document pages that are placed on
  each media side.
- `CUPS_ORIENTATION`: Controls the orientation of document pages placed on the
  media: `CUPS_ORIENTATION_PORTRAIT` or `CUPS_ORIENTATION_LANDSCAPE`.
- `CUPS_PRINT_COLOR_MODE`: Controls whether the output is in color
  \(`CUPS_PRINT_COLOR_MODE_COLOR`), grayscale
  \(`CUPS_PRINT_COLOR_MODE_MONOCHROME`), or either
  \(`CUPS_PRINT_COLOR_MODE_AUTO`).
- `CUPS_PRINT_QUALITY`: Controls the generate quality of the output:
  `CUPS_PRINT_QUALITY_DRAFT`, `CUPS_PRINT_QUALITY_NORMAL`, or
  `CUPS_PRINT_QUALITY_HIGH`.
- `CUPS_SIDES`: Controls whether prints are placed on one or both sides of the
  media: `CUPS_SIDES_ONE_SIDED`, `CUPS_SIDES_TWO_SIDED_PORTRAIT`, or
  `CUPS_SIDES_TWO_SIDED_LANDSCAPE`.

If the `value` argument is `NULL`, the `cupsCheckDestSupported` function returns
whether the option is supported by the destination.  Otherwise, the function
returns whether the specified value of the option is supported.

The `cupsFindDestSupported` function returns the IPP attribute containing the
supported values for a given option:

     ipp_attribute_t *
     cupsFindDestSupported(http_t *http, cups_dest_t *dest,
                           cups_dinfo_t *dinfo,
                           const char *option);

For example, the following code prints the supported finishing processes for a
destination, if any, to the standard output:

    cups_dinfo_t *info = cupsCopyDestInfo(CUPS_HTTP_DEFAULT,
                                          dest);

    if (cupsCheckDestSupported(CUPS_HTTP_DEFAULT, dest, info,
                               CUPS_FINISHINGS, NULL))
    {
      ipp_attribute_t *finishings =
          cupsFindDestSupported(CUPS_HTTP_DEFAULT, dest, info,
                                CUPS_FINISHINGS);
      int i, count = ippGetCount(finishings);

      puts("finishings supported:");
      for (i = 0; i < count; i ++)
        printf("  %d\n", ippGetInteger(finishings, i));
    }
    else
      puts("finishings not supported.");

The "job-creation-attributes" option can be queried to get a list of supported
options.  For example, the following code prints the list of supported options
to the standard output:

    ipp_attribute_t *attrs =
        cupsFindDestSupported(CUPS_HTTP_DEFAULT, dest, info,
                              "job-creation-attributes");
    int i, count = ippGetCount(attrs);

    for (i = 0; i < count; i ++)
      puts(ippGetString(attrs, i, NULL));


### Getting Default Values

There are two sets of default values - user defaults that are available via the
`num_options` and `options` members of the `cups_dest_t` structure, and
destination defaults that available via the `cups_dinfo_t` structure and the
`cupsFindDestDefault` function which returns the IPP attribute containing the
default value(s) for a given option:

    ipp_attribute_t *
    cupsFindDestDefault(http_t *http, cups_dest_t *dest,
                        cups_dinfo_t *dinfo,
                        const char *option);

The user defaults from `cupsGetOption` should always take preference over the
destination defaults.  For example, the following code prints the default
finishings value(s) to the standard output:

    const char *def_value =
        cupsGetOption(CUPS_FINISHINGS, dest->num_options,
                      dest->options);
    ipp_attribute_t *def_attr =
        cupsFindDestDefault(CUPS_HTTP_DEFAULT, dest, info,
                            CUPS_FINISHINGS);

    if (def_value != NULL)
    {
      printf("Default finishings: %s\n", def_value);
    }
    else
    {
      int i, count = ippGetCount(def_attr);

      printf("Default finishings: %d",
             ippGetInteger(def_attr, 0));
      for (i = 1; i < count; i ++)
        printf(",%d", ippGetInteger(def_attr, i));
      putchar('\n');
    }


### Getting Ready (Loaded) Values

The finishings and media options also support queries for the ready, or loaded,
values.  For example, a printer may have punch and staple finishers installed
but be out of staples - the supported values will list both punch and staple
finishing processes but the ready values will only list the punch processes.
Similarly, a printer may support hundreds of different sizes of media but only
have a single size loaded at any given time - the ready values are limited to
the media that is actually in the printer.

The `cupsFindDestReady` function finds the IPP attribute containing the ready
values for a given option:

    ipp_attribute_t *
    cupsFindDestReady(http_t *http, cups_dest_t *dest,
                      cups_dinfo_t *dinfo, const char *option);

For example, the following code lists the ready finishing processes:

    ipp_attribute_t *ready_finishings =
        cupsFindDestReady(CUPS_HTTP_DEFAULT, dest, info,
                          CUPS_FINISHINGS);

    if (ready_finishings != NULL)
    {
      int i, count = ippGetCount(ready_finishings);

      puts("finishings ready:");
      for (i = 0; i < count; i ++)
        printf("  %d\n", ippGetInteger(ready_finishings, i));
    }
    else
      puts("no finishings are ready.");


### Media Size Options

CUPS provides functions for querying the dimensions and margins for each of the
supported media size options.  The `cups_size_t` structure is used to describe a
media size:

    typedef struct cups_size_s
    {
      char media[128];
      int width, length;
      int bottom, left, right, top;
    } cups_size_t;

The `width` and `length` members specify the dimensions of the media in
hundredths of millimeters (1/2540th of an inch).  The `bottom`, `left`, `right`,
and `top` members specify the margins of the printable area, also in hundredths
of millimeters.

The `cupsGetDestMediaByName` and `cupsGetDestMediaBySize` functions lookup the
media size information using a standard media size name or dimensions in
hundredths of millimeters:

    int
    cupsGetDestMediaByName(http_t *http, cups_dest_t *dest,
                           cups_dinfo_t *dinfo,
                           const char *media,
                           unsigned flags, cups_size_t *size);

    int
    cupsGetDestMediaBySize(http_t *http, cups_dest_t *dest,
                           cups_dinfo_t *dinfo,
                           int width, int length,
                           unsigned flags, cups_size_t *size);

The `media`, `width`, and `length` arguments specify the size to lookup.  The
`flags` argument specifies a bitfield controlling various lookup options:

- `CUPS_MEDIA_FLAGS_DEFAULT`: Find the closest size supported by the printer.
- `CUPS_MEDIA_FLAGS_BORDERLESS`: Find a borderless size.
- `CUPS_MEDIA_FLAGS_DUPLEX`: Find a size compatible with two-sided printing.
- `CUPS_MEDIA_FLAGS_EXACT`: Find an exact match for the size.
- `CUPS_MEDIA_FLAGS_READY`: If the printer supports media sensing or
  configuration of the media in each tray/source, find the size amongst the
  "ready" media.

If a matching size is found for the destination, the size information is stored
in the structure pointed to by the `size` argument and 1 is returned.  Otherwise
0 is returned.

For example, the following code prints the margins for two-sided printing on US
Letter media:

    cups_size_t size;

    if (cupsGetDestMediaByName(CUPS_HTTP_DEFAULT, dest, info,
                               CUPS_MEDIA_LETTER,
                               CUPS_MEDIA_FLAGS_DUPLEX, &size))
    {
      puts("Margins for duplex US Letter:");
      printf("  Bottom: %.2fin\n", size.bottom / 2540.0);
      printf("    Left: %.2fin\n", size.left / 2540.0);
      printf("   Right: %.2fin\n", size.right / 2540.0);
      printf("     Top: %.2fin\n", size.top / 2540.0);
    }
    else
      puts("Margins for duplex US Letter are not available.");

You can also enumerate all of the sizes that match a given `flags` value using
the `cupsGetDestMediaByIndex` and `cupsGetDestMediaCount` functions:

    int
    cupsGetDestMediaByIndex(http_t *http, cups_dest_t *dest,
                            cups_dinfo_t *dinfo, int n,
                            unsigned flags, cups_size_t *size);

    int
    cupsGetDestMediaCount(http_t *http, cups_dest_t *dest,
                          cups_dinfo_t *dinfo, unsigned flags);

For example, the following code prints the list of ready media and corresponding
margins:

    cups_size_t size;
    int i;
    int count = cupsGetDestMediaCount(CUPS_HTTP_DEFAULT,
                                      dest, info,
                                      CUPS_MEDIA_FLAGS_READY);

    for (i = 0; i < count; i ++)
    {
      if (cupsGetDestMediaByIndex(CUPS_HTTP_DEFAULT, dest, info,
                                  i, CUPS_MEDIA_FLAGS_READY,
                                  &size))
      {
        printf("%s:\n", size.name);
        printf("   Width: %.2fin\n", size.width / 2540.0);
        printf("  Length: %.2fin\n", size.length / 2540.0);
        printf("  Bottom: %.2fin\n", size.bottom / 2540.0);
        printf("    Left: %.2fin\n", size.left / 2540.0);
        printf("   Right: %.2fin\n", size.right / 2540.0);
        printf("     Top: %.2fin\n", size.top / 2540.0);
      }
    }

Finally, the `cupsGetDestMediaDefault` function returns the default media size:

    int
    cupsGetDestMediaDefault(http_t *http, cups_dest_t *dest,
                            cups_dinfo_t *dinfo, unsigned flags,
                            cups_size_t *size);


### Localizing Options and Values

CUPS provides three functions to get localized, human-readable strings in the
user's current locale for options and values: `cupsLocalizeDestMedia`,
`cupsLocalizeDestOption`, and `cupsLocalizeDestValue`:

    const char *
    cupsLocalizeDestMedia(http_t *http, cups_dest_t *dest,
                          cups_dinfo_t *info, unsigned flags,
                          cups_size_t *size);

    const char *
    cupsLocalizeDestOption(http_t *http, cups_dest_t *dest,
                           cups_dinfo_t *info,
                           const char *option);

    const char *
    cupsLocalizeDestValue(http_t *http, cups_dest_t *dest,
                          cups_dinfo_t *info,
                          const char *option, const char *value);


## Submitting a Print Job

Once you are ready to submit a print job, you create a job using the
`cupsCreateDestJob` function:

    ipp_status_t
    cupsCreateDestJob(http_t *http, cups_dest_t *dest,
                      cups_dinfo_t *info, int *job_id,
                      const char *title, int num_options,
                      cups_option_t *options);

The `title` argument specifies a name for the print job such as "My Document".
The `num_options` and `options` arguments specify the options for the print
job which are allocated using the `cupsAddOption` function.

When successful, the job's numeric identifier is stored in the integer pointed
to by the `job_id` argument and `IPP_STATUS_OK` is returned.  Otherwise, an IPP
error status is returned.

For example, the following code creates a new job that will print 42 copies of a
two-sided US Letter document:

    int job_id = 0;
    int num_options = 0;
    cups_option_t *options = NULL;

    num_options = cupsAddOption(CUPS_COPIES, "42",
                                num_options, &options);
    num_options = cupsAddOption(CUPS_MEDIA, CUPS_MEDIA_LETTER,
                                num_options, &options);
    num_options = cupsAddOption(CUPS_SIDES,
                                CUPS_SIDES_TWO_SIDED_PORTRAIT,
                                num_options, &options);

    if (cupsCreateDestJob(CUPS_HTTP_DEFAULT, dest, info,
                          &job_id, "My Document", num_options,
                          options) == IPP_STATUS_OK)
      printf("Created job: %d\n", job_id);
    else
      printf("Unable to create job: %s\n",
             cupsLastErrorString());

Once the job is created, you submit documents for the job using the
`cupsStartDestDocument`, `cupsWriteRequestData`, and `cupsFinishDestDocument`
functions:

    http_status_t
    cupsStartDestDocument(http_t *http, cups_dest_t *dest,
                          cups_dinfo_t *info, int job_id,
                          const char *docname,
                          const char *format,
                          int num_options,
                          cups_option_t *options,
                          int last_document);

    http_status_t
    cupsWriteRequestData(http_t *http, const char *buffer,
                         size_t length);

    ipp_status_t
    cupsFinishDestDocument(http_t *http, cups_dest_t *dest,
                           cups_dinfo_t *info);

The `docname` argument specifies the name of the document, typically the
original filename.  The `format` argument specifies the MIME media type of the
document, including the following constants:

- `CUPS_FORMAT_JPEG`: "image/jpeg"
- `CUPS_FORMAT_PDF`: "application/pdf"
- `CUPS_FORMAT_POSTSCRIPT`: "application/postscript"
- `CUPS_FORMAT_TEXT`: "text/plain"

The `num_options` and `options` arguments specify per-document print options,
which at present must be 0 and `NULL`.  The `last_document` argument specifies
whether this is the last document in the job.

For example, the following code submits a PDF file to the job that was just
created:

    FILE *fp = fopen("filename.pdf", "rb");
    size_t bytes;
    char buffer[65536];

    if (cupsStartDestDocument(CUPS_HTTP_DEFAULT, dest, info,
                              job_id, "filename.pdf", 0, NULL,
                              1) == HTTP_STATUS_CONTINUE)
    {
      while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
        if (cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer,
                                 bytes) != HTTP_STATUS_CONTINUE)
          break;

      if (cupsFinishDestDocument(CUPS_HTTP_DEFAULT, dest,
                                 info) == IPP_STATUS_OK)
        puts("Document send succeeded.");
      else
        printf("Document send failed: %s\n",
               cupsLastErrorString());
    }

    fclose(fp);


# Sending IPP Requests

CUPS provides a rich API for sending IPP requests to the scheduler or printers,
typically from management or utility applications whose primary purpose is not
to send print jobs.


## Connecting to the Scheduler or Printer

The connection to the scheduler or printer is represented by the HTTP connection
type `http_t`.  The `cupsConnectDest` function connects to the scheduler or
printer associated with the destination:

    http_t *
    cupsConnectDest(cups_dest_t *dest, unsigned flags, int msec,
                    int *cancel, char *resource,
                    size_t resourcesize, cups_dest_cb_t cb,
                    void *user_data);

The `dest` argument specifies the destination to connect to.

The `flags` argument specifies whether you want to connect to the scheduler
(`CUPS_DEST_FLAGS_NONE`) or device/printer (`CUPS_DEST_FLAGS_DEVICE`) associated
with the destination.

The `msec` argument specifies how long you are willing to wait for the
connection to be established in milliseconds.  Specify a value of `-1` to wait
indefinitely.

The `cancel` argument specifies the address of an integer variable that can be
set to a non-zero value to cancel the connection.  Specify a value of `NULL`
to not provide a cancel variable.

The `resource` and `resourcesize` arguments specify the address and size of a
character string array to hold the path to use when sending an IPP request.

The `cb` and `user_data` arguments specify a destination callback function that
returns 1 to continue connecting or 0 to stop.  The destination callback work
the same way as the one used for the `cupsEnumDests` function.

On success, a HTTP connection is returned that can be used to send IPP requests
and get IPP responses.

For example, the following code connects to the printer associated with a
destination with a 30 second timeout:

    char resource[256];
    http_t *http = cupsConnectDest(dest, CUPS_DEST_FLAGS_DEVICE,
                                   30000, NULL, resource,
                                   sizeof(resource), NULL, NULL);


## Creating an IPP Request

IPP requests are represented by the IPP message type `ipp_t` and each IPP
attribute in the request is representing using the type `ipp_attribute_t`.  Each
IPP request includes an operation code (`IPP_OP_CREATE_JOB`,
`IPP_OP_GET_PRINTER_ATTRIBUTES`, etc.) and a 32-bit integer identifier.

The `ippNewRequest` function creates a new IPP request:

    ipp_t *
    ippNewRequest(ipp_op_t op);

The `op` argument specifies the IPP operation code for the request.  For
example, the following code creates an IPP Get-Printer-Attributes request:

    ipp_t *request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);

The request identifier is automatically set to a unique value for the current
process.

Each IPP request starts with two IPP attributes, "attributes-charset" and
"attributes-natural-language", followed by IPP attribute(s) that specify the
target of the operation.  The `ippNewRequest` automatically adds the correct
"attributes-charset" and "attributes-natural-language" attributes, but you must
add the target attribute(s).  For example, the following code adds the
"printer-uri" attribute to the IPP Get-Printer-Attributes request to specify
which printer is being queried:

    const char *printer_uri = cupsGetOption("device-uri",
                                            dest->num_options,
                                            dest->options);

    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
                 "printer-uri", NULL, printer_uri);

> **Note:**
>
> If we wanted to query the scheduler instead of the device, we would look
> up the "printer-uri-supported" option instead of the "device-uri" value.

The `ippAddString` function adds the "printer-uri" attribute the the IPP
request.  The `IPP_TAG_OPERATION` argument specifies that the attribute is part
of the operation.  The `IPP_TAG_URI` argument specifies that the value is a
Universal Resource Identifier (URI) string.  The `NULL` argument specifies there
is no language (English, French, Japanese, etc.) associated with the string, and
the `printer_uri` argument specifies the string value.

The IPP Get-Printer-Attributes request also supports an IPP attribute called
"requested-attributes" that lists the attributes and values you are interested
in.  For example, the following code requests the printer state attributes:

    static const char * const requested_attributes[] =
    {
      "printer-state",
      "printer-state-message",
      "printer-state-reasons"
    };

    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "requested-attributes", 3, NULL,
                  requested_attributes);

The `ippAddStrings` function adds an attribute with one or more strings, in this
case three.  The `IPP_TAG_KEYWORD` argument specifies that the strings are
keyword values, which are used for attribute names.  All strings use the same
language (`NULL`), and the attribute will contain the three strings in the
array `requested_attributes`.

CUPS provides many functions to adding attributes of different types:

- `ippAddBoolean` adds a boolean (`IPP_TAG_BOOLEAN`) attribute with one value.
- `ippAddInteger` adds an enum (`IPP_TAG_ENUM`) or integer (`IPP_TAG_INTEGER`)
  attribute with one value.
- `ippAddIntegers` adds an enum or integer attribute with one or more values.
- `ippAddOctetString` adds an octetString attribute with one value.
- `ippAddOutOfBand` adds a admin-defined (`IPP_TAG_ADMINDEFINE`), default
  (`IPP_TAG_DEFAULT`), delete-attribute (`IPP_TAG_DELETEATTR`), no-value
  (`IPP_TAG_NOVALUE`), not-settable (`IPP_TAG_NOTSETTABLE`), unknown
  (`IPP_TAG_UNKNOWN`), or unsupported (`IPP_TAG_UNSUPPORTED_VALUE`) out-of-band
  attribute.
- `ippAddRange` adds a rangeOfInteger attribute with one range.
- `ippAddRanges` adds a rangeOfInteger attribute with one or more ranges.
- `ippAddResolution` adds a resolution attribute with one resolution.
- `ippAddResolutions` adds a resolution attribute with one or more resolutions.
- `ippAddString` adds a charset (`IPP_TAG_CHARSET`), keyword (`IPP_TAG_KEYWORD`),
  mimeMediaType (`IPP_TAG_MIMETYPE`), name (`IPP_TAG_NAME` and
  `IPP_TAG_NAMELANG`), naturalLanguage (`IPP_TAG_NATURAL_LANGUAGE`), text
  (`IPP_TAG_TEXT` and `IPP_TAG_TEXTLANG`), uri (`IPP_TAG_URI`), or uriScheme
  (`IPP_TAG_URISCHEME`) attribute with one value.
- `ippAddStrings` adds a charset, keyword, mimeMediaType, name, naturalLanguage,
  text, uri, or uriScheme attribute with one or more values.


## Sending the IPP Request

Once you have created the IPP request, you can send it using the
`cupsDoRequest` function.  For example, the following code sends the IPP
Get-Printer-Attributes request to the destination and saves the response:

    ipp_t *response = cupsDoRequest(http, request, resource);

For requests like Send-Document that include a file, the `cupsDoFileRequest`
function should be used:

    ipp_t *response = cupsDoFileRequest(http, request, resource,
                                        filename);

Both `cupsDoRequest` and `cupsDoFileRequest` free the IPP request.  If a valid
IPP response is received, it is stored in a new IPP message (`ipp_t`) and
returned to the caller.  Otherwise `NULL` is returned.

The status from the most recent request can be queried using the `cupsLastError`
function, for example:

    if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST)
    {
      /* request failed */
    }

A human-readable error message is also available using the `cupsLastErrorString`
function:

    if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST)
    {
      /* request failed */
      printf("Request failed: %s\n", cupsLastErrorString());
    }


## Processing the IPP Response

Each response to an IPP request is also an IPP message (`ipp_t`) with its own
IPP attributes (`ipp_attribute_t`) that includes a status code (`IPP_STATUS_OK`,
`IPP_STATUS_ERROR_BAD_REQUEST`, etc.) and the corresponding 32-bit integer
identifier from the request.

For example, the following code finds the printer state attributes and prints
their values:

    ipp_attribute_t *attr;

    if ((attr = ippFindAttribute(response, "printer-state",
                                 IPP_TAG_ENUM)) != NULL)
    {
      printf("printer-state=%s\n",
             ippEnumString("printer-state", ippGetInteger(attr, 0)));
    }
    else
      puts("printer-state=unknown");

    if ((attr = ippFindAttribute(response, "printer-state-message",
                                 IPP_TAG_TEXT)) != NULL)
    {
      printf("printer-state-message=\"%s\"\n",
             ippGetString(attr, 0, NULL)));
    }

    if ((attr = ippFindAttribute(response, "printer-state-reasons",
                                 IPP_TAG_KEYWORD)) != NULL)
    {
      int i, count = ippGetCount(attr);

      puts("printer-state-reasons=");
      for (i = 0; i < count; i ++)
        printf("    %s\n", ippGetString(attr, i, NULL)));
    }

The `ippGetCount` function returns the number of values in an attribute.

The `ippGetInteger` and `ippGetString` functions return a single integer or
string value from an attribute.

The `ippEnumString` function converts a enum value to its keyword (string)
equivalent.

Once you are done using the IPP response message, free it using the `ippDelete`
function:

    ippDelete(response);


## Authentication

CUPS normally handles authentication through the console.  GUI applications
should set a password callback using the `cupsSetPasswordCB2` function:

    void
    cupsSetPasswordCB2(cups_password_cb2_t cb, void *user_data);

The password callback will be called when needed and is responsible for setting
the current user name using `cupsSetUser` and returning a string:

    const char *
    cups_password_cb2(const char *prompt, http_t *http,
                      const char *method, const char *resource,
                      void *user_data);

The `prompt` argument is a string from CUPS that should be displayed to the
user.

The `http` argument is the connection hosting the request that is being
authenticated.  The password callback can call the `httpGetField` and
`httpGetSubField` functions to look for additional details concerning the
authentication challenge.

The `method` argument specifies the HTTP method used for the request and is
typically "POST".

The `resource` argument specifies the path used for the request.

The `user_data` argument provides the user data pointer from the
`cupsSetPasswordCB2` call.