From a8f3c328031c8f685bd96c4efcfdd4b43a2bee9f Mon Sep 17 00:00:00 2001 From: Gerhard Muth Date: Fri, 20 Sep 2024 15:24:51 +0200 Subject: [PATCH] support saving printer icons --- README.md | 2 +- .../de/gmuth/ipp/client/IppInspector.kt | 104 ++++++++++-------- .../kotlin/de/gmuth/ipp/client/IppPrinter.kt | 26 ++++- .../de/gmuth/ipp/client/IppPrinterTests.kt | 2 +- .../de/gmuth/ipp/client/inspectPrinter.kt | 2 +- 5 files changed, 83 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 80637444..ab083030 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ IPP traffic is saved to directory `inspected-printers`. The workflow will try to ``` // need an IPP server? https://openprinting.github.io/cups/doc/man-ippeveprinter.html -IppInspector.inspect(URI.create("ipp://ippeveprinter:8501/ipp/print")) +IppInspector().inspect(URI.create("ipp://ippeveprinter:8501/ipp/print")) ``` ### Exchange [IppRequest](https://github.com/gmuth/ipp-client-kotlin/blob/master/src/main/kotlin/de/gmuth/ipp/core/IppRequest.kt) for [IppResponse](https://github.com/gmuth/ipp-client-kotlin/blob/master/src/main/kotlin/de/gmuth/ipp/core/IppResponse.kt) diff --git a/src/main/kotlin/de/gmuth/ipp/client/IppInspector.kt b/src/main/kotlin/de/gmuth/ipp/client/IppInspector.kt index 81bc1c56..ecebdb35 100644 --- a/src/main/kotlin/de/gmuth/ipp/client/IppInspector.kt +++ b/src/main/kotlin/de/gmuth/ipp/client/IppInspector.kt @@ -1,7 +1,7 @@ package de.gmuth.ipp.client /** - * Copyright (c) 2023 Gerhard Muth + * Copyright (c) 2023-2024 Gerhard Muth */ import de.gmuth.ipp.attributes.* @@ -11,25 +11,31 @@ import java.io.File import java.net.URI import java.util.logging.Logger.getLogger -object IppInspector { - private val logger = getLogger(javaClass.name) - var directory: String = "inspected-printers" - - const val pdfA4 = "blank_A4.pdf" - - fun inspect(printerUri: URI, cancelJob: Boolean = true) = - IppPrinter(printerUri, getPrinterAttributesOnInit = false).inspect(cancelJob) - - private fun IppPrinter.getPrinterModel() = StringBuilder().run { - // use another IppPrinter instance to leave request-id-counter untouched - with(IppPrinter(printerUri, getPrinterAttributesOnInit = false)) { - updateAttributes("cups-version", "printer-make-and-model") - if (isCups()) append("CUPS-") - append(makeAndModel.text.replace("\\s+".toRegex(), "_")) +class IppInspector { + + companion object { + const val pdfA4 = "blank_A4.pdf" + var directory: File = File("inspected-printers") + private val logger = getLogger(javaClass.name) + private fun getModel(printerUri: URI) = StringBuilder().run { + // use another IppPrinter instance to leave request-id-counter untouched + with(IppPrinter(printerUri, getPrinterAttributesOnInit = false)) { + updateAttributes("cups-version", "printer-make-and-model") + if (isCups()) append("CUPS-") + append(makeAndModel.text.replace("\\s+".toRegex(), "_")) + } + toString() } - toString() } + fun inspect( + printerUri: URI, + cancelJob: Boolean = true, + savePrinterIcons: Boolean = true + ) = + IppPrinter(printerUri, getPrinterAttributesOnInit = false) + .inspect(cancelJob, savePrinterIcons) + /** * Exchange a few IPP requests and save the IPP responses returned by the printer. * Operations: @@ -37,12 +43,13 @@ object IppInspector { * - Print-Job, Get-Jobs, Get-Job-Attributes * - Hold-Job, Release-Job, Cancel-Job */ - private fun IppPrinter.inspect(cancelJob: Boolean) { + private fun IppPrinter.inspect(cancelJob: Boolean, savePrinterIcons: Boolean) { logger.info { "Inspect printer $printerUri" } - val printerModel = getPrinterModel() + val printerModel = getModel(printerUri) logger.info { "Printer model: $printerModel" } + ippConfig.userName = "ipp-inspector" ippClient.saveMessages = true ippClient.saveMessagesDirectory = File(directory, printerModel).createDirectoryIfNotExists() workDirectory = ippClient.saveMessagesDirectory @@ -75,15 +82,14 @@ object IppInspector { } } - ippConfig.userName = "ipp-inspector" - runInspectionWorkflow(pdfResource, cancelJob) - } - - private fun IppPrinter.runInspectionWorkflow(pdfResource: String, cancelJob: Boolean) { + if (savePrinterIcons && attributes.containsKey("printer-icons")) { + logger.info { "> Save Printer icons" } + savePrinterIcons() + } if (supportsOperations(CupsGetPPD)) { logger.info { "> CUPS Get PPD" } - savePPD(file = File(workDirectory, "0-${name.text}.ppd")) + savePPD(file = File(workDirectory, "$printerModel.ppd")) } if (supportsOperations(IdentifyPrinter)) { @@ -108,36 +114,42 @@ object IppInspector { logger.info { response.toString() } logger.info { "> Print job $pdfResource" } - printJob( - IppInspector::class.java.getResourceAsStream("/$pdfResource")!!, - jobName(pdfResource), - - ).run { + val documentStream = IppInspector::class.java.getResourceAsStream("/$pdfResource")!! + printJob(documentStream, jobName(pdfResource)).run { logger.info { toString() } - logger.info { "> Get jobs" } for (job in getJobs()) { logger.info { "$job" } } + inspect(cancelJob) + } + } - if (supportsOperations(HoldJob, ReleaseJob)) { - logger.info { "> Hold job" } - hold() - logger.info { "> Release job" } - release() - } + private fun IppJob.inspect(cancelJob: Boolean) { - if (cancelJob) { - logger.info { "> Cancel job" } - cancel() - } + if (printer.supportsOperations(HoldJob, ReleaseJob)) { + logger.info { "> Hold job" } + hold() + logger.info { "> Release job" } + release() + } - logger.info { "> Update job attributes" } - updateAttributes() + if (cancelJob) { + logger.info { "> Cancel job" } + cancel() + } + + logger.info { "> Update job attributes" } + updateAttributes() - ippClient.saveMessages = false - logger.info { "> Wait for termination" } - waitForTermination() + printer.ippClient.saveMessages = false + logger.info { "> Wait for termination" } + waitForTermination() + + if (!cancelJob) { + printer.ippClient.saveMessages = true + logger.info { "> Get last attributes of terminated job" } + updateAttributes() } } } \ No newline at end of file diff --git a/src/main/kotlin/de/gmuth/ipp/client/IppPrinter.kt b/src/main/kotlin/de/gmuth/ipp/client/IppPrinter.kt index abbe619e..ffc80634 100644 --- a/src/main/kotlin/de/gmuth/ipp/client/IppPrinter.kt +++ b/src/main/kotlin/de/gmuth/ipp/client/IppPrinter.kt @@ -579,11 +579,11 @@ class IppPrinter( fun log(logger: Logger, level: Level = INFO) = attributes.log(logger, level, title = "PRINTER $name ($makeAndModel)") - // ----------------------- - // Save printer attributes - // ----------------------- + // ----------------------------------------- + // Save printer attributes and printer icons + // ----------------------------------------- - fun savePrinterAttributes(directory: String = ".") { + fun savePrinterAttributes(directory: File = workDirectory) { val printerModel: String = makeAndModel.text.replace("\\s+".toRegex(), "_") exchange(ippRequest(GetPrinterAttributes)).run { saveBytes(File(directory, "$printerModel.bin")) @@ -591,12 +591,30 @@ class IppPrinter( } } + fun savePrinterIcons(): Collection = attributes + .getValues>("printer-icons") + .map { it.save() } + fun printerDirectory(printerName: String = name.text.replace("\\s+".toRegex(), "_")): File = File(workDirectory, printerName).createDirectoryIfNotExists() + + // -------------------------------------------------- + // Internal utilities implemented as Kotlin extension + // -------------------------------------------------- + internal fun File.createDirectoryIfNotExists(throwOnFailure: Boolean = true) = this.apply { if (!mkdirs() && !isDirectory) "Failed to create directory: $path".let { if (throwOnFailure) throw IOException(it) else logger.warning(it) } } + + internal fun URI.save( + directory: File? = workDirectory, + fileName: String = Regex(".*/").replace(path, ""), + file: File = File(directory, fileName) + ) = file.also { + toURL().openConnection().inputStream.copyTo(file.outputStream()) + logger.info { "Saved ${file.absolutePath} (${file.length()} bytes from $this)" } + } } \ No newline at end of file diff --git a/src/test/kotlin/de/gmuth/ipp/client/IppPrinterTests.kt b/src/test/kotlin/de/gmuth/ipp/client/IppPrinterTests.kt index 8b885e27..691c92d9 100644 --- a/src/test/kotlin/de/gmuth/ipp/client/IppPrinterTests.kt +++ b/src/test/kotlin/de/gmuth/ipp/client/IppPrinterTests.kt @@ -85,7 +85,7 @@ class IppPrinterTests { @Test fun savePrinterAttributes() { ippClientMock.mockResponse("Get-Printer-Attributes.ipp") - printer.savePrinterAttributes(createTempDirectory().pathString) + printer.savePrinterAttributes(createTempDirectory().toFile()) } @Test diff --git a/src/test/kotlin/de/gmuth/ipp/client/inspectPrinter.kt b/src/test/kotlin/de/gmuth/ipp/client/inspectPrinter.kt index 9e7e8bf2..fe8d6be9 100644 --- a/src/test/kotlin/de/gmuth/ipp/client/inspectPrinter.kt +++ b/src/test/kotlin/de/gmuth/ipp/client/inspectPrinter.kt @@ -11,7 +11,7 @@ fun main() { val printerURI = URI.create("ipp://xero.local") try { - IppInspector.inspect(printerURI, cancelJob = true) + IppInspector().inspect(printerURI, cancelJob = true) } catch (throwable: Throwable) { logger.log(Level.SEVERE, "Failed to inspect printer $printerURI", throwable) }