Skip to content

Commit f9440c4

Browse files
committed
First (incomplete) version of the new (lispkit pdf) library.
1 parent 839dc2c commit f9440c4

File tree

8 files changed

+2163
-37
lines changed

8 files changed

+2163
-37
lines changed

File.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
static let burnInAnnotationsOption: PDFDocumentWriteOption
2+
static let optimizeImagesForScreenOption: PDFDocumentWriteOption
3+
static let saveImagesAsJPEGOption: PDFDocumentWriteOption
4+
static let saveTextFromOCROption: PDFDocumentWriteOption

LispKit.xcodeproj/project.pbxproj

+14-8
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,8 @@
13441344
CCF602ED23A0FA9A00087740 /* pack.sld in Copy pre-installed LispKit library: SRFI 167 */ = {isa = PBXBuildFile; fileRef = CCF602E723A0F6B900087740 /* pack.sld */; };
13451345
CCF602EE23A0FA9A00087740 /* memory.sld in Copy pre-installed LispKit library: SRFI 167 */ = {isa = PBXBuildFile; fileRef = CCF602E823A0F6CB00087740 /* memory.sld */; };
13461346
CCF6C4172442F62800EB37AC /* VectorLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6A3B5D1C52E8EF00E962E2 /* VectorLibrary.swift */; };
1347+
CCF964052D00D66000F85FBA /* PDFLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF964042D00D65900F85FBA /* PDFLibrary.swift */; };
1348+
CCF964062D00D66000F85FBA /* PDFLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF964042D00D65900F85FBA /* PDFLibrary.swift */; };
13471349
CCF982F71D998B8A00B22A1D /* ImportSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF982F61D998B8A00B22A1D /* ImportSet.swift */; };
13481350
CCFB412725B2523C005B2429 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = CCFB412625B2523C005B2429 /* ZIPFoundation */; };
13491351
CCFB412B25B25316005B2429 /* ZipArchiveLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCFB412A25B25316005B2429 /* ZipArchiveLibrary.swift */; };
@@ -3759,6 +3761,7 @@
37593761
CCF602E723A0F6B900087740 /* pack.sld */ = {isa = PBXFileReference; lastKnownFileType = text; path = pack.sld; sourceTree = "<group>"; };
37603762
CCF602E823A0F6CB00087740 /* memory.sld */ = {isa = PBXFileReference; lastKnownFileType = text; path = memory.sld; sourceTree = "<group>"; };
37613763
CCF602E923A0F8F800087740 /* SRFI-167.scm */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SRFI-167.scm"; sourceTree = "<group>"; };
3764+
CCF964042D00D65900F85FBA /* PDFLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDFLibrary.swift; sourceTree = "<group>"; };
37623765
CCF982F61D998B8A00B22A1D /* ImportSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSet.swift; sourceTree = "<group>"; };
37633766
CCFB412A25B25316005B2429 /* ZipArchiveLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipArchiveLibrary.swift; sourceTree = "<group>"; };
37643767
CCFBE5C31ECBA8DA0032254B /* Pi.scm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Pi.scm; sourceTree = "<group>"; };
@@ -4775,6 +4778,7 @@
47754778
CCD5B6152C3BC480002BF2F0 /* HTTPOAuthLibrary.swift */,
47764779
CC1D533C2BF1709E00EE1A24 /* JSONLibrary.swift */,
47774780
CCD5B5202C0D231E002BF2F0 /* JSONSchemaLibrary.swift */,
4781+
CCF964042D00D65900F85FBA /* PDFLibrary.swift */,
47784782
CC17F7472C7DB91000DDC4F9 /* KeychainLibrary.swift */,
47794783
CC894F1F2CC3022800896B1D /* PasteboardLibrary.swift */,
47804784
CC894F222CC3B08100896B1D /* PasteboardLibrary_iOS.swift */,
@@ -5542,6 +5546,7 @@
55425546
CCC0724A1F9C11FA0063974E /* Sysctl.swift in Sources */,
55435547
CC882C921C9E93A600D20039 /* Timer.swift in Sources */,
55445548
CCAD52E21C48468F00DBD8EE /* Expr.swift in Sources */,
5549+
CCF964062D00D66000F85FBA /* PDFLibrary.swift in Sources */,
55455550
CC76312720E95262005F27CE /* DrawingDocument.swift in Sources */,
55465551
CCFB412B25B25316005B2429 /* ZipArchiveLibrary.swift in Sources */,
55475552
CC35FD0D1C6FA4B700C8B992 /* VirtualMachine.swift in Sources */,
@@ -5716,6 +5721,7 @@
57165721
CCC48CA025E5B96800D082AD /* CaptureGroup.swift in Sources */,
57175722
CC372CD327D367CC0091C474 /* BitsetLibrary.swift in Sources */,
57185723
CCC48CD325E5B98600D082AD /* ListLibrary.swift in Sources */,
5724+
CCF964052D00D66000F85FBA /* PDFLibrary.swift in Sources */,
57195725
CCC48CB125E5B97100D082AD /* Library.swift in Sources */,
57205726
CCC48CD225E5B98600D082AD /* CharSetLibrary.swift in Sources */,
57215727
CCC48CCD25E5B98600D082AD /* CoreLibrary.swift in Sources */,
@@ -5817,7 +5823,7 @@
58175823
"@executable_path/../Frameworks",
58185824
"@loader_path/Frameworks",
58195825
);
5820-
MACOSX_DEPLOYMENT_TARGET = 13.0;
5826+
MACOSX_DEPLOYMENT_TARGET = 13.4;
58215827
MARKETING_VERSION = 2.4.0;
58225828
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
58235829
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14";
@@ -5859,7 +5865,7 @@
58595865
"@executable_path/../Frameworks",
58605866
"@loader_path/Frameworks",
58615867
);
5862-
MACOSX_DEPLOYMENT_TARGET = 13.0;
5868+
MACOSX_DEPLOYMENT_TARGET = 13.4;
58635869
MARKETING_VERSION = 2.4.0;
58645870
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
58655871
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14";
@@ -5890,7 +5896,7 @@
58905896
ENABLE_USER_SCRIPT_SANDBOXING = NO;
58915897
GCC_C_LANGUAGE_STANDARD = gnu17;
58925898
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
5893-
MACOSX_DEPLOYMENT_TARGET = 13.0;
5899+
MACOSX_DEPLOYMENT_TARGET = 13.4;
58945900
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
58955901
MTL_FAST_MATH = YES;
58965902
PRODUCT_BUNDLE_IDENTIFIER = net.objecthub.LispKitRepl;
@@ -5920,7 +5926,7 @@
59205926
ENABLE_USER_SCRIPT_SANDBOXING = NO;
59215927
GCC_C_LANGUAGE_STANDARD = gnu17;
59225928
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
5923-
MACOSX_DEPLOYMENT_TARGET = 13.0;
5929+
MACOSX_DEPLOYMENT_TARGET = 13.4;
59245930
MTL_FAST_MATH = YES;
59255931
PRODUCT_BUNDLE_IDENTIFIER = net.objecthub.LispKitRepl;
59265932
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -6078,7 +6084,7 @@
60786084
"@executable_path/../Frameworks",
60796085
"@loader_path/Frameworks",
60806086
);
6081-
MACOSX_DEPLOYMENT_TARGET = 13.0;
6087+
MACOSX_DEPLOYMENT_TARGET = 13.4;
60826088
MARKETING_VERSION = 2.5.0;
60836089
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
60846090
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
@@ -6114,7 +6120,7 @@
61146120
"@executable_path/../Frameworks",
61156121
"@loader_path/Frameworks",
61166122
);
6117-
MACOSX_DEPLOYMENT_TARGET = 13.0;
6123+
MACOSX_DEPLOYMENT_TARGET = 13.4;
61186124
MARKETING_VERSION = 2.5.0;
61196125
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
61206126
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
@@ -6140,7 +6146,7 @@
61406146
"@executable_path/../Frameworks",
61416147
"@loader_path/../Frameworks",
61426148
);
6143-
MACOSX_DEPLOYMENT_TARGET = 13.0;
6149+
MACOSX_DEPLOYMENT_TARGET = 13.4;
61446150
PRODUCT_BUNDLE_IDENTIFIER = net.objecthub.LispKitTests;
61456151
PRODUCT_NAME = "$(TARGET_NAME)";
61466152
SDKROOT = macosx;
@@ -6160,7 +6166,7 @@
61606166
"@executable_path/../Frameworks",
61616167
"@loader_path/../Frameworks",
61626168
);
6163-
MACOSX_DEPLOYMENT_TARGET = 13.0;
6169+
MACOSX_DEPLOYMENT_TARGET = 13.4;
61646170
PRODUCT_BUNDLE_IDENTIFIER = net.objecthub.LispKitTests;
61656171
PRODUCT_NAME = "$(TARGET_NAME)";
61666172
SDKROOT = macosx;

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import PackageDescription
4040
let package = Package(
4141
name: "LispKit",
4242
platforms: [
43-
.macOS(.v13),
43+
.macOS("13.4"),
4444
.iOS(.v16)
4545
],
4646
products: [

Sources/LispKit/Compiler/EvalError.swift

+15
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ public enum EvalError: Int, Hashable, Codable {
193193
case urlFormatConstraintError
194194
case urlAuthorityError
195195
case urlPrototypeInvalid
196+
case cannotCreatePdf
197+
case invalidPDFDisplayBox
198+
case invalidPDFAccessIdentifier
199+
case invalidPDFDocGenerationOption
200+
case unknownPDFAnnotationType
196201

197202
public var message: String {
198203
switch self {
@@ -532,6 +537,16 @@ public enum EvalError: Int, Hashable, Codable {
532537
return "invalid URL authority specifier: $0"
533538
case .urlPrototypeInvalid:
534539
return "$0 is not a valid URL prototype for procedure $,1"
540+
case .cannotCreatePdf:
541+
return "cannot create pdf document from bytevector $0"
542+
case .invalidPDFDisplayBox:
543+
return "not a valid PDF display box specifier: $0"
544+
case .invalidPDFAccessIdentifier:
545+
return "invalid PDF access permission identifier: $0"
546+
case .invalidPDFDocGenerationOption:
547+
return "invalid PDF document generation option: $0"
548+
case .unknownPDFAnnotationType:
549+
return "unknown PDF annotation type: $0"
535550
}
536551
}
537552

Sources/LispKit/Graphics/Drawing.swift

+39-17
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import CoreGraphics
2222
import Cocoa
2323
import AppKit
24+
import PDFKit
2425

2526
///
2627
/// Class `Drawing` represents a sequence of drawing instructions. The class offers the
@@ -84,15 +85,15 @@ public final class Drawing: NativeObject {
8485
context.cgContext.endTransparencyLayer()
8586
context.restoreGraphicsState()
8687
}
87-
self.drawInline()
88+
self.drawInline(in: context)
8889
}
8990
}
9091

9192
/// Draw the drawing into the current graphics context without saving and restoring the
9293
/// graphics state.
93-
public func drawInline() {
94+
public func drawInline(in context: NSGraphicsContext) {
9495
for instruction in self.instructions {
95-
instruction.draw()
96+
instruction.draw(in: context)
9697
}
9798
}
9899

@@ -322,19 +323,28 @@ public enum DrawingInstruction {
322323
case text(String, font: NSFont?, color: Color?, style: NSParagraphStyle?, at: ObjectLocation)
323324
case attributedText(NSAttributedString, at: ObjectLocation)
324325
case image(NSImage, ObjectLocation, operation: NSCompositingOperation, opacity: Double)
326+
case page(PDFPage, PDFDisplayBox, NSRect)
325327
case inline(Drawing)
326328
case include(Drawing, clippedTo: Shape?)
327329

328-
fileprivate func draw() {
330+
/// Draw the instruction if there is a valid current drawing context
331+
public func draw() {
332+
if let context = NSGraphicsContext.current {
333+
self.draw(in: context)
334+
}
335+
}
336+
337+
/// Draw the instruction into the given drawing context
338+
public func draw(in context: NSGraphicsContext) {
329339
switch self {
330340
case .setStrokeColor(let color):
331341
color.nsColor.setStroke()
332342
case .setFillColor(let color):
333343
color.nsColor.setFill()
334344
case .setStrokeWidth(let width):
335-
NSGraphicsContext.current?.cgContext.setLineWidth(CGFloat(width))
345+
context.cgContext.setLineWidth(CGFloat(width))
336346
case .setBlendMode(let blendMode):
337-
NSGraphicsContext.current?.cgContext.setBlendMode(blendMode)
347+
context.cgContext.setBlendMode(blendMode)
338348
case .setShadow(let color, let dx, let dy, let blurRadius):
339349
let shadow = NSShadow()
340350
shadow.shadowOffset = NSSize(width: dx, height: dy)
@@ -356,20 +366,19 @@ public enum DrawingInstruction {
356366
transform.invert()
357367
transform.concat()
358368
case .strokeLine(let start, let end):
359-
if let context = NSGraphicsContext.current?.cgContext {
360-
context.beginPath()
361-
context.move(to: start)
362-
context.addLine(to: end)
363-
context.strokePath()
364-
}
369+
let context = context.cgContext
370+
context.beginPath()
371+
context.move(to: start)
372+
context.addLine(to: end)
373+
context.strokePath()
365374
case .strokeRect(let rct):
366-
NSGraphicsContext.current?.cgContext.stroke(rct)
375+
context.cgContext.stroke(rct)
367376
case .fillRect(let rct):
368-
NSGraphicsContext.current?.cgContext.fill(rct)
377+
context.cgContext.fill(rct)
369378
case .strokeEllipse(let rct):
370-
NSGraphicsContext.current?.cgContext.strokeEllipse(in: rct)
379+
context.cgContext.strokeEllipse(in: rct)
371380
case .fillEllipse(let rct):
372-
NSGraphicsContext.current?.cgContext.fillEllipse(in: rct)
381+
context.cgContext.fillEllipse(in: rct)
373382
case .stroke(let shape, let width):
374383
shape.stroke(lineWidth: width)
375384
case .strokeDashed(let shape, let width, let dashLengths, let dashPhase):
@@ -436,10 +445,23 @@ public enum DrawingInstruction {
436445
respectFlipped: true,
437446
hints: [:])
438447
}
448+
case .page(let page, let box, let rect):
449+
let context = context.cgContext
450+
context.saveGState()
451+
// PDF coordinate system is Y-flipped from Core Graphics
452+
context.translateBy(x: rect.origin.x, y: rect.origin.y + rect.height)
453+
// Apply the PDF's crop box transform
454+
let bounds = page.bounds(for: box)
455+
page.transform(context, for: box)
456+
// Scale PDF to view size
457+
context.scaleBy(x: rect.width / bounds.width, y: -rect.height / bounds.height)
458+
// Draw
459+
page.draw(with: box, to: context)
460+
context.restoreGState()
439461
case .include(let drawing, let clippingRegion):
440462
drawing.draw(clippedTo: clippingRegion)
441463
case .inline(let drawing):
442-
drawing.drawInline()
464+
drawing.drawInline(in: context)
443465
}
444466
}
445467

Sources/LispKit/Primitives/DrawingLibrary.swift

+51-9
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,14 @@ public final class DrawingLibrary: NativeLibrary {
237237
self.define(Procedure("rect?", isRect))
238238
self.define(Procedure("rect", rect))
239239
self.define(Procedure("rect-point", rectPoint))
240-
self.define(Procedure("rect-size", rectSize))
241240
self.define(Procedure("rect-x", rectX))
242241
self.define(Procedure("rect-y", rectY))
242+
self.define(Procedure("rect-size", rectSize))
243243
self.define(Procedure("rect-width", rectWidth))
244244
self.define(Procedure("rect-height", rectHeight))
245+
self.define(Procedure("rect-max-point", rectMaxPoint))
246+
self.define(Procedure("rect-max-x", rectMaxX))
247+
self.define(Procedure("rect-max-y", rectMaxY))
245248
self.define(Procedure("move-rect", moveRect))
246249

247250
// Utilities
@@ -924,7 +927,7 @@ public final class DrawingLibrary: NativeLibrary {
924927
}
925928
}
926929

927-
private func makeBitmap(drawing: Expr, size: Expr, dpi: Expr?) throws -> Expr {
930+
private func makeBitmap(expr: Expr, size: Expr, dpi: Expr?, ipol: Expr?) throws -> Expr {
928931
guard case .pair(.flonum(let w), .flonum(let h)) = size, w > 0.0 && h > 0.0 else {
929932
throw RuntimeError.eval(.invalidSize, size)
930933
}
@@ -949,27 +952,44 @@ public final class DrawingLibrary: NativeLibrary {
949952
bytesPerRow: 0,
950953
bitsPerPixel: 0) else {
951954
throw RuntimeError.eval(.cannotCreateBitmap,
952-
.pair(drawing, .pair(size, .pair(dpi ?? .fixnum(72), .null))))
955+
.pair(expr, .pair(size, .pair(dpi ?? .fixnum(72), .null))))
953956
}
954957
// Set the intended size of the image (vs. size of the bitmap above)
955958
bitmap.size = NSSize(width: w, height: h)
956959
// Create a graphics context for drawing into the bitmap
957960
guard let context = NSGraphicsContext(bitmapImageRep: bitmap) else {
958961
throw RuntimeError.eval(.cannotCreateBitmap,
959-
.pair(drawing, .pair(size, .pair(dpi ?? .fixnum(72), .null))))
962+
.pair(expr, .pair(size, .pair(dpi ?? .fixnum(72), .null))))
960963
}
961964
let previous = NSGraphicsContext.current
962965
// Create a flipped graphics context if required
963966
NSGraphicsContext.current = NSGraphicsContext(cgContext: context.cgContext, flipped: true)
964-
let transform = NSAffineTransform()
965-
transform.translateX(by: 0.0, yBy: CGFloat(h))
966-
transform.scaleX(by: 1.0, yBy: -1.0)
967-
transform.concat()
968967
defer {
969968
NSGraphicsContext.current = previous
970969
}
970+
// Set interpolation quality
971+
if let ipol,
972+
let quality = CGInterpolationQuality(rawValue: Int32(try ipol.asInt(above: 0, below: 5))) {
973+
NSGraphicsContext.current?.cgContext.interpolationQuality = quality
974+
}
971975
// Draw into the bitmap
972-
try self.drawing(from: drawing).draw()
976+
switch expr {
977+
case .object(let obj):
978+
if let imageBox = obj as? NativeImage {
979+
imageBox.value.draw(in: NSRect(origin: .zero, size: bitmap.size),
980+
from: .zero,
981+
operation: .copy,
982+
fraction: 1.0)
983+
break
984+
}
985+
fallthrough
986+
default:
987+
let transform = NSAffineTransform()
988+
transform.translateX(by: 0.0, yBy: CGFloat(h))
989+
transform.scaleX(by: 1.0, yBy: -1.0)
990+
transform.concat()
991+
try self.drawing(from: expr).draw()
992+
}
973993
// Create an image and add the bitmap as a representation
974994
let nsimage = NSImage(size: bitmap.size)
975995
nsimage.addRepresentation(bitmap)
@@ -1763,6 +1783,14 @@ public final class DrawingLibrary: NativeLibrary {
17631783
return point
17641784
}
17651785

1786+
private func rectMaxPoint(expr: Expr) throws -> Expr {
1787+
guard case .pair(.pair(.flonum(let x), .flonum(let y)),
1788+
.pair(.flonum(let w), .flonum(let h))) = expr else {
1789+
throw RuntimeError.eval(.invalidRect, expr)
1790+
}
1791+
return .pair(.flonum(x + w), .flonum(y + h))
1792+
}
1793+
17661794
private func rectX(expr: Expr) throws -> Expr {
17671795
guard case .pair(.pair(.flonum(let x), .flonum(_)), .pair(.flonum(_), .flonum(_))) = expr else {
17681796
throw RuntimeError.eval(.invalidRect, expr)
@@ -1777,6 +1805,20 @@ public final class DrawingLibrary: NativeLibrary {
17771805
return .flonum(y)
17781806
}
17791807

1808+
private func rectMaxX(expr: Expr) throws -> Expr {
1809+
guard case .pair(.pair(.flonum(let x), .flonum(_)), .pair(.flonum(let w), .flonum(_))) = expr else {
1810+
throw RuntimeError.eval(.invalidRect, expr)
1811+
}
1812+
return .flonum(x + w)
1813+
}
1814+
1815+
private func rectMaxY(expr: Expr) throws -> Expr {
1816+
guard case .pair(.pair(.flonum(_), .flonum(let y)), .pair(.flonum(_), .flonum(let h))) = expr else {
1817+
throw RuntimeError.eval(.invalidRect, expr)
1818+
}
1819+
return .flonum(y + h)
1820+
}
1821+
17801822
private func rectSize(expr: Expr) throws -> Expr {
17811823
guard case .pair(.pair(.flonum(_), .flonum(_)), let size) = expr else {
17821824
throw RuntimeError.eval(.invalidRect, expr)

0 commit comments

Comments
 (0)