ipp-nio: Swift 互联网打印协议

一个纯 Swift 语言实现的 互联网打印协议 (IPP),基于 swift-nioasync-http-client

这个库允许您直接与几乎任何网络打印机通信,无需任何驱动程序或操作系统依赖。它提供了一个简单的 API,用于编码和交换 IPP 1.1 请求和响应,以及一种灵活、Swift 风格的方式来处理强类型属性。

警告: 这个软件包是全新出炉的,您将成为测试阶段的一部分。请参阅下面的 实现状态

ipp-nio 添加到您的软件包

// Add to package dependencies
.package(url: "https://github.com/sliemeobn/ipp-nio.git", from: "0.1.0"),
// Add to your target depencies
dependencies: [
    .product(name: "IppClient", package: "ipp-nio"),
]

特性

该库的功能集大致分为四个层次

IppProtocol 模块

IppClient 模块

用法

打印 PDF

import struct Foundation.Data
import IppClient

let printer = IppPrinter(
    httpClient: HTTPClient(configuration: .init(certificateVerification: .none)),
    uri: "ipps://my-printer/ipp/print"
)

let pdf = try Data(contentsOf: .init(fileURLWithPath: "myfile.pdf"))

let response = try await printer.printJob(
    documentFormat: "application/pdf",
    data: .bytes(pdf)
)

if response.statusCode.class == .successful {
    print("Print job submitted")
} else {
    print("Print job failed with status \(response.statusCode) \(response[operation: \.statusMessage])")
}

设置作业模板属性

var jobAttributes = IppAttributes()
jobAttributes[\.jobTemplate.copies] = 2
jobAttributes[\.jobTemplate.orientationRequested] = .landscape
jobAttributes["some-custom-thing"] = .init(.keyword("some-value"))

let response = try await printer.printJob(
    documentName: "myfile.pdf",
    jobAttributes: jobAttributes,
    data: .stream(myFileAsStream)
)

请求作业状态

let response = try await printer.printJob(data: .bytes(myData))
guard let jobId = response[job: \.jobId] else { exit(1) }

let job = printer.job(jobId)

while true {
    let response = try await job.getJobAttributes(requestedAttributes: [.jobState])
    guard let jobState = response[job: \.jobState] else {
        print("Failed to get job state")
        exit(1)
    }

    switch jobState {
    case .aborted, .canceled, .completed:
        print("Job ended with state \(jobState)")
        exit(0)
    default:
        print("Job state is \(jobState)")
    }

    try await Task.sleep(for: .seconds(3))
}

设置身份验证

// "basic" mode
let printer = IppPrinter(
    httpClient: HTTPClient(configuration: .init(certificateVerification: .none)),
    uri: "ipps://my-printer/ipp/print",
    authentication: .basic(username: "user", password: "top-secret")
)

// "requesting-user" mode
let printer = IppPrinter(
    httpClient: HTTPClient(configuration: .init(certificateVerification: .none)),
    uri: "ipps://my-printer/ipp/print",
    authentication: .requestingUser(username: "user")
)

使用原始负载

import IppProtocol
import NIOCore

var request = IppRequest(
        version: .v1_1,
        operationId: .holdJob,
        requestId: 1
    )

request[.operation][.attributesCharset] = .init(.charset("utf-8"))
request[.operation][.attributesNaturalLanguage] = .init(.naturalLanguage("en-us"))
request[.operation][.printerUri] = .init(.uri("ipp://:631/printers/ipp-printer"))
request[.job]["my-crazy-attribute"] = .init(.enumValue(420), .enumValue(69))

var bytes = ByteBuffer()
request.write(to: &bytes)
let read = try! IppRequest(buffer: &bytes)

print(request == read) // true

我的打印机的 IPP URL 是什么?

大多数打印机都可以通过 DNS-SD/Bonjour 发现,任何 DNS-SD 浏览器都应该显示它们的信息。(例如:macOS 的 Discovery)。

rp 值是 URL 路径(通常是 /ipp/print),方案始终是 ipp://ipps://

在 macOS 上,共享打印机也通过 IPP 公开。(即:任何打印机都可以是网络打印机,中间有一个服务器)

实现状态

基本的、低级编码和传输是健壮的,应该可以满足所有需求。语义模型目前仅涵盖最基本的属性,但可以根据需要轻松扩展。

由于该库在编写时考虑了自定义扩展,因此即使没有直接支持,也应该很容易扩展到任何用例。

缺失

您想添加任何内容吗?请随时联系我,也欢迎 “pull request” ^^