diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4a99c1..682bb4a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,5 +15,7 @@ jobs: - uses: swift-actions/setup-swift@v2 with: swift-version: "6.2" + - name: Test + run: swift test --configuration release -v - name: Build run: swift build --configuration release -v diff --git a/Package.swift b/Package.swift index 92e9bd2..f21ea37 100644 --- a/Package.swift +++ b/Package.swift @@ -15,6 +15,10 @@ let package = Package( ) ], targets: [ - .target(name: "PDFViewKit") + .target(name: "PDFViewKit"), + .testTarget( + name: "PDFViewKitTests", + dependencies: ["PDFViewKit"] + ) ] ) diff --git a/Sources/PDFViewKit/PDFRenderer.swift b/Sources/PDFViewKit/PDFRenderer.swift index 0a7ef5a..7deda5e 100644 --- a/Sources/PDFViewKit/PDFRenderer.swift +++ b/Sources/PDFViewKit/PDFRenderer.swift @@ -16,11 +16,12 @@ public enum PDFRenderer { to destination: URL, atPageSize pageSize: some PageSize ) throws { - let pdfSize = pageSize.size(atDPI: .print) + let pdfSize = pageSize.size(atDPI: .pdf) let viewRenderingDPI: DPI = .display let viewSize = pageSize.size(atDPI: viewRenderingDPI) - let scaleFactor = pdfSize.width / viewSize.width + let viewToPDFScaleFactor = pdfSize.width / viewSize.width + let rasterizationScaleFactor = DPI.print.rawValue / viewRenderingDPI.rawValue var box = CGRect( origin: .zero, size: pdfSize @@ -38,12 +39,12 @@ public enum PDFRenderer { let renderer = ImageRenderer(content: renderedView) renderer.proposedSize = ProposedViewSize(viewSize) - renderer.render(rasterizationScale: scaleFactor) { _, renderer in + renderer.render(rasterizationScale: rasterizationScaleFactor) { _, renderer in context.beginPDFPage(nil) context.scaleBy( - x: scaleFactor, - y: scaleFactor + x: viewToPDFScaleFactor, + y: viewToPDFScaleFactor ) renderer(context) diff --git a/Sources/PDFViewKit/PageSizes/DPI.swift b/Sources/PDFViewKit/PageSizes/DPI.swift index 41ce4c8..4e1eaa5 100644 --- a/Sources/PDFViewKit/PageSizes/DPI.swift +++ b/Sources/PDFViewKit/PageSizes/DPI.swift @@ -13,6 +13,8 @@ public enum DPI: CGFloat { // based on https://www.adobe.com/uk/creativecloud/photography/discover/dots-per-inch-dpi-resolution.html + case pdf = 72 // PDF user-space points (1/72 inch) + case display = 96 case displayHigh = 144 // 150% diff --git a/Tests/PDFViewKitTests/PDFRendererTests.swift b/Tests/PDFViewKitTests/PDFRendererTests.swift new file mode 100644 index 0000000..04da887 --- /dev/null +++ b/Tests/PDFViewKitTests/PDFRendererTests.swift @@ -0,0 +1,54 @@ +// +// PDFRendererTests.swift +// PDFViewKit +// +// Copyright (C) 2026 Sören Gade +// See LICENSE for full license. +// + +import Foundation +import PDFKit +@testable import PDFViewKit +import SwiftUI +import Testing + +@MainActor +struct PDFRendererTests { + + @Test + func renderedA4PageUsesPDFPointDimensions() throws { + let destination = FileManager.default.temporaryDirectory + .appendingPathComponent("PDFViewKit-A4-\(UUID().uuidString).pdf") + defer { try? FileManager.default.removeItem(at: destination) } + + let document = PDFViewKit.PDFDocument { + Text("Hello, A4") + } + + try PDFRenderer.render( + document: document, + to: destination, + atPageSize: DIN.a4 + ) + + let pdfDocument = try #require(PDFDocument(url: destination)) + let page = try #require(pdfDocument.page(at: 0)) + + let mediaBoxSize = page.bounds(for: .mediaBox).size + + #expect(abs(mediaBoxSize.width - a4SizeInPDFPoints.width) < pointTolerance) + #expect(abs(mediaBoxSize.height - a4SizeInPDFPoints.height) < pointTolerance) + } + + private var a4SizeInPDFPoints: CGSize { + CGSize( + width: 210 / 25.4 * DPI.pdf.rawValue, + height: 297 / 25.4 * DPI.pdf.rawValue + ) + } + + private var pointTolerance: CGFloat { + 0.001 + } + +}