Skip to content

Commit

Permalink
support saving printer icons
Browse files Browse the repository at this point in the history
  • Loading branch information
gmuth committed Sep 20, 2024
1 parent 02ab79e commit a8f3c32
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
104 changes: 58 additions & 46 deletions src/main/kotlin/de/gmuth/ipp/client/IppInspector.kt
Original file line number Diff line number Diff line change
@@ -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.*
Expand All @@ -11,38 +11,45 @@ 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:
* - Get-Printer-Attributes
* - 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
Expand Down Expand Up @@ -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)) {
Expand All @@ -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()
}
}
}
26 changes: 22 additions & 4 deletions src/main/kotlin/de/gmuth/ipp/client/IppPrinter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -579,24 +579,42 @@ 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"))
printerGroup.saveText(File(directory, "$printerModel.txt"))
}
}

fun savePrinterIcons(): Collection<File> = attributes
.getValues<List<URI>>("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)" }
}
}
2 changes: 1 addition & 1 deletion src/test/kotlin/de/gmuth/ipp/client/IppPrinterTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class IppPrinterTests {
@Test
fun savePrinterAttributes() {
ippClientMock.mockResponse("Get-Printer-Attributes.ipp")
printer.savePrinterAttributes(createTempDirectory().pathString)
printer.savePrinterAttributes(createTempDirectory().toFile())
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/de/gmuth/ipp/client/inspectPrinter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit a8f3c32

Please sign in to comment.