Skip to content

A client implementation of the ipp protocol written in kotlin

License

Notifications You must be signed in to change notification settings

gmuth/ipp-client-kotlin

Repository files navigation

ipp-client

A client implementation of the ipp protocol for java and kotlin. RFCs 8010, 8011, 3995 and 3996

License: MIT Build Quality Gate Status Sonar Coverage Maven Central

Usage

You may use ippfind or other ZeroConf tools for printer discovery. The CupsClient supports printer lookup by queue name. Repository ipp-samples contains examples how to use jmDNS.

// initialize printer connection and show printer attributes
val ippPrinter = IppPrinter(URI.create("ipp://colorjet.local/ipp/printer"))
ippPrinter.attributes.logDetails()

// marker levels
ippPrinter.markers.forEach { println(it) }
println("black: ${ippPrinter.marker(BLACK).levelPercent()} %")

// print file
val file = File("A4-ten-pages.pdf")
val job = ippPrinter.printJob(
    file,
    jobName(file.name),
    jobPriority(30),
    documentFormat("application/pdf"),
    copies(2),
    numberUp(2),
    pageRanges(2..3, 8..10),
    printerResolution(300),
    finishings(Punch, Staple),
    IppPrintQuality.High,
    IppColorMode.Monochrome,
    IppSides.TwoSidedLongEdge,
    media("iso_a4_210x297mm"),
    mediaColSource("tray-1"),
    notifyEvents = listOf("job-state-changed", "job-stopped", "job-completed") // CUPS
)
job.logDetails()
job.subscription?.processEvents { println(it) }

// print remote file, make printer pull document from remote server
val remoteFile = URI.create("http://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")
ippPrinter.printUri(remoteFile)

// create job and send document
val job = ippPrinter.createJob(jobName(file.name))
job.sendDocument(FileInputStream(file))
job.waitForTermination()

// manage jobs
ippPrinter.getJobs().forEach { println(it) }
ippPrinter.getJobs(IppWhichJobs.Completed)

val job = ippPrinter.getJob(4)
job.hold()
job.release()
job.cancel()
job.cupsGetDocuments() // CUPS only

// print operator
ippPrinter.pause()
ippPrinter.resume()
ippPrinter.sound() // identify printer

// subscribe and log events (e.g. from CUPS) for 1 minute
ippPrinter.createPrinterSubscription(60)
          .processEvents()

Printer Capabilities

IppPrinter checks, if attribute values are supported by looking into '...-supported' printer attributes.

documentFormat("application/pdf")

WARN: according to printer attributes value 'application/pdf' is not supported.
document-format-supported (1setOf mimeMediaType) = application/PCL,application/postscript

exchange IppRequest for IppResponse

val uri = URI.create("ipp://colorjet.local/ipp/printer")
val file = File("A4-blank.pdf")

val ippClient = IppClient()
val request = IppRequest(IppOperation.PrintJob, uri).apply {
  // constructor adds 'attributes-charset', 'attributes-natural-language' and 'printer-uri'
  operationGroup.attribute("document-format", IppTag.MimeMediaType, "application/pdf")
  documentInputStream  = FileInputStream(file)
}
val response = ippClient.exchange(request)
println(response.jobGroup["job-id"])

Use the CupsClient to connect to a CUPS server. If you want to access a cups queue you can construct an IppPrinter from it's uri.

// connect to default ipp://localhost:631
val cupsClient = CupsClient()

// credentials (e.g. for remote connections)
cupsClient.basicAuth("admin", "secret")

// list all queues
cupsClient.getPrinters().forEach { 
    println("${it.name} -> ${it.printerUri}")
}

// list all completed jobs for queue
cupsClient.getPrinter("ColorJet_HP")
          .getJobs(IppWhichJobs.Completed)
          .forEach { println(it) }

// default printer
val defaultPrinter = cupsClient.getDefault()

// check capability
if(defaultPrinter.hasCapability(Capability.CanPrintInColor)) {
    println("${defaultPrinter.name} can print in color")
}

// get canceled jobs and save documents
cupsClient.getJobsAndSaveDocuments(IppWhichJobs.Canceled)

Print jpeg to 2" label printer

val printer = IppPrinter(URI.create("ipp://192.168.2.64"))
val width = 2540 * 2 // hundreds of mm

val jpegFile = File("label.jpg")
val image = javax.imageio.ImageIO.read(jpegFile)
            
printer.printJob(
    jpegFile, documentFormat("image/jpeg"),
    IppMedia.Collection(
        size = IppMedia.Size(width, width * image.height / image.width),
        margins = IppMedia.Margins(0)
    )
)

Logging

Log levels can be changed globally or individually. The defaultLogLevel must be changed before any constructor of a logger is called.

Logging.defaultLogLevel = Logging.LogLevel.ERROR
IppInputStream.log.logLevel = Logging.LogLevel.DEBUG
IppOutputStream.log.logLevel = Logging.LogLevel.TRACE

A simple stdout console writer is enabled by default and can be disabled.

Logging.disable()

You can configure the library to use Java Util Logging or Slf4j. Then the log levels must be configured according to the underlaying implementation (e.g. logback or Slf4j-Android).

JulAdapter.configure()
Slf4jAdapter.configure()

Build

To build the jar make sure you have JDK 11 installed. The default tasks build the jar in build/libs.

./gradlew

To install the artifact to your local maven repository run

./gradlew publishToMavenLocal

This software has no dependencies to javax.print, CUPS or ipptool. Operation has mostly been tested for target jvm. Android is supported since v1.6. A Java Version 11 Runtime is only required if you want to use the Java 11 HttpClient.

Artifact coordinates

The build produces the jar, sources and javadoc artifacts. They are available at maven central repository.

  • group: gmuth.de
  • artifact: ipp-client
  • version: 2.4

Add dependency:

    implementation("de.gmuth:ipp-client:2.4")

No Multiplatform support yet

IPP is based on the exchange of binary messages via HTTP. For reading and writing binary data DataInputStream and DataOutputStream are used.

For the transport layer I've created a HTTP interface. By default implementation HttpURLConnectionClient is used which in turn uses javas HttpURLConnection.

Only java runtimes (including Android) provide implementations of these classes. The java standard libraries also provide support for SSL/TLS.

Source packages

Package de.gmuth.ipp.core contains the usual encoding and decoding operations. RFC 8010 is fully supported.

Package de.gmuth.ipp.client contains the IppClient which requires a Http.Client that implements HTTP: e.g. HttpURLConnectionClient or JavaHttpClient