Skip to content

Commit

Permalink
Define abstract PageSize
Browse files Browse the repository at this point in the history
This allows for easy extensibility of custom page sizes as well as for
defining more standard sizes.
  • Loading branch information
sgade committed Jun 12, 2024
1 parent b82d2a8 commit 5602c72
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 37 deletions.
6 changes: 3 additions & 3 deletions Sources/PDFViewKit/EnvironmentKeys/PDFPageSizeKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import SwiftUI

struct PDFPageSizeKey: EnvironmentKey {

static var defaultValue: DIN = .a4
static var defaultValue: PageSize = DIN.a4

}

// MARK: - EnvironmentValues shorthand extension

public extension EnvironmentValues {

var pdfPageSize: DIN {
var pdfPageSize: PageSize {
get { self[PDFPageSizeKey.self] }
set { self[PDFPageSizeKey.self] = newValue }
}
Expand All @@ -39,7 +39,7 @@ public extension View {
/// - size: The page size for the view and its descendants.
/// - force: Whether to apply the size to the view's frame.
func pdfPageSize(
_ size: DIN,
_ size: PageSize,
force: Bool = false
) -> some View {
environment(\.pdfPageSize, size)
Expand Down
4 changes: 2 additions & 2 deletions Sources/PDFViewKit/Modifiers/ApplyPDFSizeFrameModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SwiftUI

private struct ApplyPDFSizeFrameModifier: ViewModifier {

public let size: DIN
public let size: PageSize

@Environment(\.pdfRenderingDPI)
private var pdfRenderingDPI
Expand All @@ -32,7 +32,7 @@ extension View {
/// Applies the given PDF size to the view's frame, taking the current DPI setting into account.
///
/// - Seealso: ``PDFRenderingDPIEnvironmentKey``
func applyPDFSizeFrame(size: DIN) -> some View {
func applyPDFSizeFrame(size: PageSize) -> some View {
modifier(ApplyPDFSizeFrameModifier(size: size))
}

Expand Down
26 changes: 22 additions & 4 deletions Sources/PDFViewKit/PDFDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ public struct PDFDocument {

}

// MARK: Default page size for result builders

private extension PDFDocument {

/// The default size used for pages implicitly created from views.
static let defaultPageSize: PageSize = DIN.a4

}

// MARK: - PDFPagesBuilder

@resultBuilder
Expand All @@ -42,15 +51,21 @@ public enum PDFPagesBuilder {
}

public static func buildPartialBlock(first: some View) -> [PDFPage<AnyView>] {
[PDFPage(erasing: first)]
[PDFPage(
size: PDFDocument.defaultPageSize,
erasing: first
)]
}

public static func buildPartialBlock(
accumulated: [PDFPage<AnyView>],
next: some View
) -> [PDFPage<AnyView>] {
var pages = accumulated
pages.append(PDFPage(erasing: next))
pages.append(PDFPage(
size: PDFDocument.defaultPageSize,
erasing: next
))
return pages
}

Expand All @@ -73,15 +88,18 @@ public enum PDFDocumentBuilder {

public static func buildPartialBlock(first: some View) -> PDFDocument {
PDFDocument {
PDFPage {
PDFPage(size: PDFDocument.defaultPageSize) {
first
}
}
}

public static func buildPartialBlock(accumulated: PDFDocument, next: some View) -> PDFDocument {
var pages = accumulated.pages
pages.append(PDFPage(erasing: next))
pages.append(PDFPage(
size: PDFDocument.defaultPageSize,
erasing: next
))
return PDFDocument(pages: pages)
}

Expand Down
15 changes: 10 additions & 5 deletions Sources/PDFViewKit/PDFPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import SwiftUI
/// The alignment of the page's content is top leading.
public struct PDFPage<Content: View>: View {

public let size: DIN
public let size: PageSize

public let content: Content

public init(
size: DIN = .a4,
size: PageSize,
@ViewBuilder _ content: () -> Content
) {
self.size = size
Expand Down Expand Up @@ -53,16 +53,21 @@ public extension PDFPage where Content == AnyView {
}
}

init(erasing view: some View) {
self.init(erasing: PDFPage({ AnyView(erasing: view) }))
init(
size: PageSize,
erasing view: some View
) {
self.init(erasing: PDFPage(size: size) {
AnyView(erasing: view)
})
}

}

// MARK: - Previews

#Preview {
PDFPage {
PDFPage(size: DIN.a4) {
Text("Hello, World")
}
}
2 changes: 1 addition & 1 deletion Sources/PDFViewKit/PDFRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum PDFRenderer {
public static func render(
document: PDFDocument,
to destination: URL,
atPageSize pageSize: DIN
atPageSize pageSize: some PageSize
) throws {
let pdfSize = pageSize.size(atDPI: .print)
let viewRenderingDPI: DPI = .display
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

import Foundation

// Source: https://de.wikipedia.org/wiki/Papierformat#Internationale_Papierformate_(ISO/DIN)
/// Defines standard page sizes in the german DIN system.
public enum DIN {

// Source: https://de.wikipedia.org/wiki/Papierformat#Internationale_Papierformate_(ISO/DIN)

case a0
case a1
case a2
Expand All @@ -25,11 +27,11 @@ public enum DIN {

}

// MARK: Physical page sizes
// MARK: PageSize

public extension DIN {
extension DIN: PageSize {

var width: Measurement<UnitLength> {
public var width: Measurement<UnitLength> {
switch self {
case .a0:
Measurement(
Expand Down Expand Up @@ -99,7 +101,7 @@ public extension DIN {
}
}

var height: Measurement<UnitLength> {
public var height: Measurement<UnitLength> {
switch self {
case .a0:
Measurement(
Expand Down Expand Up @@ -169,20 +171,3 @@ public extension DIN {
}

}

// MARK: Calculated properties based on physical sizes

public extension DIN {

var aspectRatio: CGFloat {
width.converted(to: .millimeters).value / height.converted(to: .millimeters).value
}

func size(atDPI dpi: DPI) -> CGSize {
CGSize(
width: width.converted(to: .inches).value * dpi.rawValue,
height: height.converted(to: .inches).value * dpi.rawValue
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

import Foundation

/// Defines specific dots per inch resolutions.
public enum DPI: CGFloat {

// based on https://www.adobe.com/uk/creativecloud/photography/discover/dots-per-inch-dpi-resolution.html

case display = 96
case displayHigh = 144 // 150%

Expand Down
46 changes: 46 additions & 0 deletions Sources/PDFViewKit/PageSizes/PageSize.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// PageSize.swift
// PDFViewKit
//
// Copyright (C) 2024 Sören Gade
// See LICENSE for full license.
//

import Foundation

/// A type that defines page sizes.
public protocol PageSize {

/// The page's physical width.
var width: Measurement<UnitLength> { get }

/// The page's physical height.
var height: Measurement<UnitLength> { get }

/// The page's size aspect ratio.
var aspectRatio: CGFloat { get }

/// Calculates the page's dot size at the given ``DPI`` rendering scale.
///
/// - Parameters:
/// - dpi: The page resolution of dots per inch.
func size(atDPI dpi: DPI) -> CGSize

}

// MARK: Default implementation

public extension PageSize {

var aspectRatio: CGFloat {
width.converted(to: .millimeters).value / height.converted(to: .millimeters).value
}

func size(atDPI dpi: DPI) -> CGSize {
CGSize(
width: width.converted(to: .inches).value * dpi.rawValue,
height: height.converted(to: .inches).value * dpi.rawValue
)
}

}

0 comments on commit 5602c72

Please sign in to comment.