Skip to content

Add CUPS printing system bindings to c.s.j.p.unix#1718

Merged
dbwiddis merged 1 commit into
java-native-access:masterfrom
dbwiddis:add-cups-bindings
May 10, 2026
Merged

Add CUPS printing system bindings to c.s.j.p.unix#1718
dbwiddis merged 1 commit into
java-native-access:masterfrom
dbwiddis:add-cups-bindings

Conversation

@dbwiddis
Copy link
Copy Markdown
Contributor

@dbwiddis dbwiddis commented May 9, 2026

Adds Cups interface to c.s.j.p.unix providing JNA bindings for libcups, the Common UNIX Printing System library available on Linux, macOS, and other UNIX platforms.

Structures

Structure C type Description
CupsDest cups_dest_t Printer/class destination with name, instance, and options
CupsOption cups_option_t Name/value option pair
CupsJob cups_job_t Print job with ID, state, title, user, and timestamps

Functions

Function Description
cupsGetDests / cupsGetDests2 Enumerate available printers
cupsFreeDests Free destination list
cupsGetDest Find a printer in a list by name
cupsGetNamedDest Optimized single-printer lookup
cupsGetDefault Get default printer name
cupsGetOption Read an option value
cupsAddOption Add/replace an option
cupsFreeOptions Free options array
cupsGetJobs2 Get print jobs (active/completed/all)
cupsFreeJobs Free job array
cupsCancelJob Cancel a print job
cupsServer / cupsSetServer Get/set CUPS server
cupsUser / cupsSetUser Get/set CUPS user
cupsLastError / cupsLastErrorString Error reporting

Constants

  • Printer state: IPP_PRINTER_IDLE, IPP_PRINTER_PROCESSING, IPP_PRINTER_STOPPED
  • Printer type flags: CUPS_PRINTER_CLASS, CUPS_PRINTER_COLOR, CUPS_PRINTER_DUPLEX, etc.
  • Job state: IPP_JSTATE_PENDING through IPP_JSTATE_COMPLETED
  • Job filters: CUPS_WHICHJOBS_ALL, CUPS_WHICHJOBS_ACTIVE, CUPS_WHICHJOBS_COMPLETED

Closes #1710

Test Output

On my local mac with actual printers:

[junit] Testsuite: com.sun.jna.platform.CupsTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.393 sec
[junit] 
[junit] ------------- Standard Output ---------------
[junit] Default named dest: HP_ENVY_6000_series
[junit] Number of CUPS destinations: 2
[junit] First destination: SecurePrintAMER
[junit]   printer-info: Secure Print AMER
[junit]   printer-state: 3
[junit]   printer-type: 2109692
[junit] CUPS server: /private/var/run/cupsd
[junit] Number of CUPS jobs: 50
[junit] First job: id=4 dest=HP_ENVY_6000_series title=Microsoft Word - Guidelines.docx state=9
[junit] CUPS user: dbwiddis
[junit] Default printer: HP_ENVY_6000_series
[junit] ------------- ---------------- ---------------

@dbwiddis dbwiddis force-pushed the add-cups-bindings branch 4 times, most recently from 9730d3f to 2e49a2e Compare May 9, 2026 21:47
@dbwiddis dbwiddis requested a review from matthiasblaesing May 9, 2026 21:52
Copy link
Copy Markdown
Member

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks sane to me. For the documentation references people need to be careful. I noticed for example for CupsJob the structure definition in the documentation did not match the header file. The mapping is sane with respect to the header, so is good.

You might want to consider this change:

--- a/contrib/platform/test/com/sun/jna/platform/CupsTest.java
+++ b/contrib/platform/test/com/sun/jna/platform/CupsTest.java
@@ -27,6 +27,9 @@
 import com.sun.jna.platform.unix.Cups;
 import com.sun.jna.Pointer;
 import com.sun.jna.ptr.PointerByReference;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
 
 import junit.framework.TestCase;
 
@@ -88,7 +91,8 @@
                 assertNotNull("Destinations pointer should not be null", destsPtr);
 
                 // Read the first destination
-                Cups.CupsDest dest = new Cups.CupsDest(destsPtr);
+                Cups.CupsDest[] dests = (Cups.CupsDest[]) new Cups.CupsDest(destsPtr).toArray(numDests);
+                Cups.CupsDest dest = dests[0];
                 assertNotNull("First destination name should not be null", dest.name);
                 System.out.println("First destination: " + dest.name
                         + (dest.is_default != 0 ? " (default)" : ""));
@@ -106,7 +110,7 @@
 
                     String printerType = Cups.INSTANCE.cupsGetOption(
                             "printer-type", dest.num_options, dest.options);
-                    System.out.println("  printer-type: " + printerType);
+                    System.out.println("  printer-type: " + printerType + " " + mapPrinterTypeEnumNames(printerType).toString());
                 }
             }
         } finally {
@@ -116,6 +120,28 @@
         }
     }
 
+    public List<String> mapPrinterTypeEnumNames(String printerType) throws RuntimeException, NumberFormatException {
+        try {
+            List<String> result = new ArrayList<>();
+
+            int printerTypeInt = Integer.parseInt(printerType);
+
+            for (Field f : Cups.class.getFields()) {
+                String fieldName = f.getName();
+                if (fieldName.startsWith("CUPS_PRINTER_")) {
+                    int val = f.getInt(null);
+                    if (((printerTypeInt & val) == val)) {
+                        result.add(fieldName);
+                    }
+                }
+            }
+
+            return result;
+        } catch (IllegalAccessException | IllegalArgumentException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
     public void testGetDefault() {
         if (!cupsAvailable) {
             return;
@@ -144,7 +170,8 @@
                 Pointer jobsPtr = jobsRef.getValue();
                 assertNotNull("Jobs pointer should not be null", jobsPtr);
 
-                Cups.CupsJob job = new Cups.CupsJob(jobsPtr);
+                Cups.CupsJob[] jobs = (Cups.CupsJob[]) new Cups.CupsJob(jobsPtr).toArray(numJobs);
+                Cups.CupsJob job = jobs[0];
                 assertTrue("Job ID should be positive", job.id > 0);
                 assertNotNull("Job destination should not be null", job.dest);
                 System.out.println("First job: id=" + job.id + " dest=" + job.dest

Both cupsGetDests and cupsGetJobs2 return an array of values and this demonstrates decoding. The code in mapPrinterTypeEnumNames was more for me to test what I get for my default printer.

Closes java-native-access#1710

Add Cups interface providing JNA bindings for libcups, the Common UNIX
Printing System library. Includes structures (CupsDest, CupsOption,
CupsJob), printer state/type constants, job state constants, and
functions for destination enumeration, job management, option handling,
and server/user configuration.
@dbwiddis dbwiddis force-pushed the add-cups-bindings branch from 2e49a2e to 801734b Compare May 10, 2026 13:55
@dbwiddis
Copy link
Copy Markdown
Contributor Author

dbwiddis commented May 10, 2026

For the documentation references people need to be careful ... the structure definition in the documentation did not match the header file.

Yeah, I've seen that a lot in manpage docs as well, where they tell you what elements are in the structure (assuming you'll fetch via struct.foo in C) but don't give the correct order.

Applied your suggested changes, will merge when CI passes.

@dbwiddis dbwiddis merged commit f103aa3 into java-native-access:master May 10, 2026
12 checks passed
@dbwiddis dbwiddis deleted the add-cups-bindings branch May 10, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New feature suggestions,

2 participants