I try to rotate a page 180° in a pdf file.
I nearly get it, but the page is also mirrored horizontally.
Some images to illustrate:
Initial page:
Result after rotation (see code): it is rotated 180° BUT mirrored horizontally as well:
The expected result
It is just as if it was rotated 180°, around the x axis of the page. And I would need to rotate 180° around z axis (perpendicular to the page). It is probably the result of
writeContext!.scaleBy(x: 1, y: -1)
I have tried a lot of changes for transform, translate, scale parameters, including removing calls to some of them, to no avail.
@IBAction func createNewPDF(_ sender: UIButton) {
var originalPdfDocument: CGPDFDocument!
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = urls[0]
// read some pdf from bundle for test
if let path = Bundle.main.path(forResource: "Test", ofType: "pdf"), let pdf = CGPDFDocument(URL(fileURLWithPath: path) as CFURL) {
originalPdfDocument = pdf
} else { return }
// create new pdf
let modifiedPdfURL = documentsDirectory.appendingPathComponent("Modified.pdf")
guard let page = originalPdfDocument.page(at: 1) else { return } // Starts at page 1
var mediaBox: CGRect = page.getBoxRect(CGPDFBox.mediaBox) // mediabox which will set the height and width of page
let writeContext = CGContext(modifiedPdfURL as CFURL, mediaBox: &mediaBox, nil) // get the context
var pageRect: CGRect = page.getBoxRect(CGPDFBox.mediaBox) // get the page rect
writeContext!.beginPage(mediaBox: &pageRect)
let m = page.getDrawingTransform(.mediaBox, rect: mediaBox, rotate: 0, preserveAspectRatio: true) // Because of rotate 0, no effect ; changed rotate to 180, then get an empty page
writeContext!.translateBy(x: 0, y: pageRect.size.height)
writeContext!.scaleBy(x: 1, y: -1)
writeContext!.concatenate(m)
writeContext!.clip(to: pageRect)
writeContext!.drawPDFPage(page) // draw content in page
writeContext!.endPage() // end the current page
writeContext!.closePDF()
}
Note: This is a follow up of a previous thread,
https://developer.apple.com/forums/thread/688436
PDFKit
RSS for tagDisplay and manipulate PDF documents in your applications using PDFKit.
Posts under PDFKit tag
71 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Recently, our app crash monitor detect a lot of PDFKit crash problem at iOS 15.3:
MACH_Exception EXC_BREAKPOINT EXC_ARM_BREAKPOINT fault_address:0x00000001809a52d8
Thread 67 name: PDFKit.PDFTilePool.workQueue
0
CoreFoundation
_CFRetain (in CoreFoundation)
1
CoreGraphics
_CGColorRetain (in CoreGraphics)
2
PDFKit
-[PDFPage _drawWithBox:inContext:withRotation:isThumbnail:withAnnotations:withBookmark:withDelegate:] (in PDFKit)
3
PDFKit
-[PDFPage drawWithBox:inContext:isThumbnail:] (in PDFKit)
4
PDFKit
-[PDFView drawPage:toContext:] (in PDFKit)
5
PDFKit
-[PDFTilePool _renderTileForRequest:] (in PDFKit)
6
libdispatch.dylib
__dispatch_call_block_and_release (in libdispatch.dylib)
7
libdispatch.dylib
__dispatch_client_callout (in libdispatch.dylib)
8
libdispatch.dylib
__dispatch_lane_serial_drain (in libdispatch.dylib)
9
libdispatch.dylib
__dispatch_lane_invoke (in libdispatch.dylib)
10
libdispatch.dylib
__dispatch_workloop_worker_thread (in libdispatch.dylib)
11
libsystem_pthread.dylib
__pthread_wqthread (in libsystem_pthread.dylib)
12
libsystem_pthread.dylib
_start_wqthread (in libsystem_pthread.dylib)
It crash at the thread: PDFKit.PDFTilePool.workQueue
Anyone got the same problem? Is there any solutions for this problem?
I'm trying to use PDFPageOverlayViewProvider by copying the code provided in the "What's new in PDFKit" WWDC22 session here: https://developer.apple.com/videos/play/wwdc2022/10089/
I've copied the method implementations and set my pdfView's pageOverlayViewProvider property to the view where I implemented the protocol. However, when I try to run my app, the pdfView(_ view: PDFView, overlayViewFor page: PDFPage) method is never getting called.
Has anyone been able to get this working successfully?
https://developer.apple.com/videos/play/wwdc2022/10089/
I am trying to run codes about PDFPageOverlayViewProvider, but the codes are not working. I cannot see what I wrote or annotate. Anyone know how can I solve and make this code working?
func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
var resultView: PKCanvasView? = nil
if let overlayView = pageToViewMapping[page] {
resultView = (overlayView as! PKCanvasView)
} else {
let canvasView = PKCanvasView(frame: .zero)
canvasView.drawingPolicy = .anyInput
canvasView.tool = PKInkingTool(.pen, color: .yellow, width: 20)
canvasView.backgroundColor = .clear
pageToViewMapping[page] = canvasView
resultView = canvasView
}
let page = page as! WatermarkPage
if let drawing = page.drawing {
resultView?.drawing = drawing
}
return resultView
}
func pdfView(_ pdfView: PDFView, willEndDisplayingOverlayView overlayView: UIView, for page: PDFPage) {
let overlayView = overlayView as! PKCanvasView
let page = page as! WatermarkPage
page.drawing = overlayView.drawing
pageToViewMapping.removeValue(forKey: page)
}
Crashed: com.apple.main-thread
0 CoreFoundation 0x7c59c CFArrayGetCount + 8
1 CorePDF 0x28c1c CGPDFTaggedNodeEnumerateChildren + 52
2 CorePDF 0x28b70 CGPDFTaggedNodeGetBounds + 244
3 PDFKit 0x6400 (Missing UUID 4dedb563f2df3dceae5066d6b4718b9f)
4 UIAccessibility 0x818e0 ___axuiElementForNotificationData_block_invoke + 56
Hello. There is a "Highlight" context menu option that shows up when a user right-clicks some selected text in a PDF. There is no way to get rid of this menu item and clicking it does nothing. How do I get rid of it?
I tried overriding buildMenu(with builder: UIMenuBuilder) to remove it but that doesn't work. Help!
I'm using PDFKit to show a PDF which contains some hidden fields. Unfortunately, PDFKit seems to ignore that property, annotation.shouldDisplay is always YES.
I have to set shouldDisplay by my own depending on the annotation's internal flags:
// see Chapter 8.4.2 Annotation Flags in PDF Reference for PDF 1.7
// https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.7old.pdf
static const NSUInteger PDFAnnotationFlagInvisible = 1;
static const NSUInteger PDFAnnotationFlagHidden = 1 << 1;
// ...
for (PDFAnnotation* annotation in page.annotations) {
id value = [annotation valueForAnnotationKey:PDFAnnotationKeyFlags];
if (value != nil) {
NSInteger annotationFlags = [value integerValue];
if (annotationFlags & (PDFAnnotationFlagInvisible | PDFAnnotationFlagHidden)) {
annotation.shouldDisplay = NO;
}
}
}
It doesn't feel right that this snippet is needed. So is this a bug / known issue in PDFKit or is my PDF somehow "wrong".
I am using QLPreviewController with SwiftUI using UIViewControllerRepresentable. If I try to delete or insert pages of large size PDF, QLPreviewController is not calling delegate methods (didUpdateContentsOf, didSaveEditedCopyOf).
struct QuickLookController: UIViewControllerRepresentable {
@Environment(\.dismiss) var dismiss
weak var delegate: QLPreviewControllerDelegate?
weak var dataSource: QLPreviewControllerDataSource?
func makeUIViewController(context: Context) -> UINavigationController {
let controller = context.coordinator.controller
controller.delegate = delegate
controller.dataSource = dataSource
controller.navigationItem.rightBarButtonItem = context.coordinator.dismissButton
return UINavigationController(rootViewController: controller)
}
func updateUIViewController(_ viewController: UINavigationController, context: UIViewControllerRepresentableContext<QuickLookController>) { }
func makeCoordinator() -> Self.Coordinator {
.init(parent: self)
}
@MainActor
class Coordinator: NSObject {
var parent: QuickLookController
init(parent: QuickLookController) {
self.parent = parent
}
lazy var controller: QLPreviewController = {
let controller = QLPreviewController()
return controller
}()
lazy var dismissButton: UIBarButtonItem = {
let button = UIBarButtonItem(
title: NSLocalizedString("Done", comment: ""),
style: .plain,
target: self,
action: #selector(rightButtonTapped(_:))
)
button.tag = 2
return button
}()
@objc func rightButtonTapped(_ sender: Any) {
controller.dismiss(animated: true)
}
}
}
// MARK: QuickLook
extension ViewerModel: QLPreviewControllerDataSource, QLPreviewControllerDelegate {
public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
1
}
public func previewController(
_ controller: QLPreviewController,
previewItemAt index: Int
) -> QLPreviewItem {
let title = self.document
.documentURL?
.lastPathComponent ?? ""
let url = PDFManager
.directory
.appendingPathComponent(title) as NSURL
return url as QLPreviewItem
}
public func previewControllerDidDismiss(_ controller: QLPreviewController) {
}
public func previewControllerWillDismiss(_ controller: QLPreviewController) {
}
✔️ It's same even if I set it to updateContents
public func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
.createCopy
}
✔️ Not called with Large Size PDF
public func previewController(_ controller: QLPreviewController, didUpdateContentsOf previewItem: QLPreviewItem) {
}
✔️ Not called with Large Size PDF
public func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) {
}
}
Hi,
I'm trying to use PencilKit over PDFKit as described in https://developer.apple.com/videos/play/wwdc2022/10089/. The thing is I open my custom UIDocument and initialize all its content to feed PDFView. Everything seems to work, I Input sketches in the canvas, PDFPageOverlayViewProvider's overlayView(for:) generates canvas correctly (it seems) but when editing finishes : willEndDisplayingOverlayView never gets called, and when I save the UIDocument (I use document.close(completionHandler:)) contents(forType:) never sees my custom PDFPages and I get no content for sketches.
Does anyone of you have an idea of the lifecycle we should follow to get the methods called ?
Sincerely yours
Hello there, I am trying to follow along with the video and copy the example shown here in SwiftUI. I am given the error Cannot assign value of type 'UIView' to type 'PKCanvasView?' on this line: resultView = overlayView
It is totally possible that I am botching the whole thing up but I would appreciate it if someone looked over my code. Thanks.
code:
// ContentView.swift
import SwiftUI
import PDFKit
import PencilKit
import Foundation
import UIKit
struct PDFUIView: View {
let pdfDoc: PDFDocument
let pdfView: PDFView
init() {
let url = Bundle.main.url(forResource: "example", withExtension: "pdf")!
pdfDoc = PDFDocument(url: url)!
pdfView = PDFView()
}
var body: some View {
VStack {
PDFKitView(showing: pdfDoc, pdfView: pdfView)
}
.padding()
}
}
#Preview {
PDFUIView()
}
struct PDFKitView: UIViewRepresentable {
let pdfDocument: PDFDocument
let pdfView: PDFView
init(showing pdfDoc: PDFDocument, pdfView:PDFView) {
self.pdfDocument = pdfDoc
self.pdfView = pdfView
}
func makeUIView(context: Context) -> PDFView {
pdfView.usePageViewController(true)
pdfView.autoScales = true
pdfView.pageOverlayViewProvider = context.coordinator
pdfView.displayMode = .singlePageContinuous
pdfView.isUserInteractionEnabled = true
pdfView.document = pdfDocument
pdfView.delegate = context.coordinator
return pdfView
}
func updateUIView(_ pdfView: PDFView, context: Context) {
pdfView.document = pdfDocument
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
}
class Coordinator: NSObject, PDFPageOverlayViewProvider, PDFViewDelegate {
var pageToViewMapping = [PDFPage: UIView]()
func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
var resultView: PKCanvasView? = nil
if let overlayView = pageToViewMapping[page] {
resultView = overlayView
} else {
var canvasView = PKCanvasView(frame: .zero)
canvasView.drawingPolicy = .anyInput
canvasView.tool = PKInkingTool(.pen, color: .systemCyan, width: 20)
canvasView.backgroundColor = UIColor.clear
pageToViewMapping[page] = canvasView
resultView = canvasView
}
let page = page as! MyPDFPage
if let drawing = page.drawing {
resultView?.drawing = drawing
}
return resultView
}
func pdfView(_ pdfView: PDFView, willDisplayOverlayView overlayView: UIView, for page: PDFPage) {
guard let overlayView = overlayView as? PKCanvasView else {
return
}
guard let canvasView = pageToViewMapping[page] else {
return
}
let page = page as! MyPDFPage
page.drawing = overlayView.drawing
pageToViewMapping.removeValue(forKey: page)
}
class MyPDFAnnotation: PDFAnnotation {
override func draw(with box: PDFDisplayBox, in context: CGContext) {
UIGraphicsPushContext(context)
context.saveGState()
let page = self.page as! MyPDFPage
if let drawing = page.drawing {
let image = drawing.image(from: drawing.bounds, scale: 1)
image.draw(in: drawing.bounds)
}
context.restoreGState()
UIGraphicsPopContext()
}
}
class MyPDFPage: PDFPage {
var drawing: PKDrawing?
}
}
Hello,
We have functionality in an existing app, have to generate a pdf and it should be printable in the A4. The PDF is almost 8 to 10 pages
I have applied below solution:
Loading the HTML template on the WKwebview and doing a few operations on the content and converting it into a pdf.
Converting the HTML into a string
Replacing a few data with dynamic content
Issue:
The solutions worked fine in the previous OS(14.5,15.5,16.1), it is not working on the latest os 16.6
The Table background CSS is not Rendering. I have tried both Inline and external CSS loading but still facing the same issue.
Approch1:
func createPDF(formmatter: UIViewPrintFormatter, filename: String) -> String {
let attributedString = NSAttributedString(string: "This is a test", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])
let printFormatter = UISimpleTextPrintFormatter(attributedText: attributedString)
let render = UIPrintPageRenderer()
render.addPrintFormatter(printFormatter, startingAtPageAt: 0)
// 2. Assign print formatter to UIPrintPageRenderer
//let render = UIPrintPageRenderer()
render.addPrintFormatter(formmatter, startingAtPageAt: 0)
// 3. Assign paperRect and printableRect
let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = page.insetBy(dx: 20, dy: 20)
//let printable = page.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10));
render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
// 4. Create PDF context and draw
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)
for i in 1...render.numberOfPages {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)
}
UIGraphicsEndPDFContext();
// 5. Save PDF file
var dst = self.getDestinationPath(1)
if dst.contains("file://") {
dst = dst.replacingOccurrences(of: "file://", with: "")
}
//let path = "\(NSTemporaryDirectory())\(filename).pdf"
pdfData.write(toFile: dst, atomically: true)
print("open \(dst)")
return dst
}
}
Approach2:
But the pdf is Generating in a single page not multiple and it's not printable using the below solution.
func createPDFMethod(webView: WKWebView, title:String="samplepdf"){
let pdfConfiguration = WKPDFConfiguration()
/// Using `webView.scrollView.frame` allows us to capture the
// entire page, not just the visible portion
pdfConfiguration.rect = CGRect(x: 0, y: 0, width: webView.scrollView.contentSize.width, height: webView.scrollView.contentSize.height)
webView.createPDF(configuration: pdfConfiguration) { result in
switch result {
case .success(let data):
// Creates a path to the downloads directory
DispatchQueue.main.async {
let resourceDocPath = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
let pdfNameFromUrl = "\(title).pdf"
let actualPath = resourceDocPath.appendingPathComponent(pdfNameFromUrl)
do {
try data.write(to: actualPath, options: .atomic)
print("pdf successfully saved!")
} catch {
print("Pdf could not be saved")
}
}
case .failure(let failure):
print(failure.localizedDescription)
}
}
}
It seems the issue is with UIGraphics or WKwebview formaters.
could you please help me to resolve this issue?
The Problem
In the Preview app in macOS (or the Files app in iOS and iPadOS), when a user selects a radio button to "On", the radio button appears to behave as expected (with related radio buttons, sharing the same parent form field, appearing to turn "Off"). Also, as expected, the app indicates that the user has edited the PDF and, as such, is able to save and close the file as normal. On re-opening the file, the radio buttons seem to have been reset and user input lost.
What is Happening
Related radio button annotation widgets share the same parent form field. In the PDF 1.7 specification (ISO PDF32000-2008, s. 12.7.4.2.4), the parent form field object holds the field name property (T), the name object of the appearance state of the kid object currently selected (V), as well as an array of the references to the related radio button kid objects (Kids). Each kid object holds a reference to the parent object (Parent). When the user selects a radio button to be on, the V property is updated in the parent object accordingly.
On saving the PDF, an incremental update to the file is made with an updated copy of the kid object, corresponding to the selected radio box, created. The kid object created for the updated radio box, however, is updated incorrectly with the Parent object reference removed and the properties that ought to reside with the parent object (for V, T, and FT), instead, incorrectly written/merged into the kid object itself. The original parent object (belonging to the shared field form) is not updated with the incremental update in any way.
Impact on User Experience
Radio buttons are not functional, with user-input not properly saved. As such, using Preview to complete a PDF form with radio buttons is not possible. On re-opening the PDF, user input to the state of radio buttons appear not to have been saved.
Affected Apps/OSs:
Preview (macOS 12 and above) and Files (iOS 16 and iPadOS 16)
Related Sample of Code
Radio button annotation widget object (11 0 obj) and form field parent object (16 0 obj) in original PDF file:
11 0 obj
<<
/Border [ 0 0 0 ]
/Rect [ 433 405 453 425 ]
/F 4
/BS 13 0 R
/Subtype /Widget
/DA (/Helvetica 13 Tf 0 g)
/MK 14 0 R /C [ 0 ]
/AP 15 0 R /M (D:20230803164805Z00'00')
/AS /Off
/Parent 16 0 R
/Type /Annot
/Ff 32768
>>
endobj
16 0 obj
<<
/V /
/Kids [ 10 0 R 11 0 R ]
/T (button2)
/FT /Btn
>>
endobj
15 0 obj
<<
/N 17 0 R
>>
endobj
17 0 obj
<<
/Ted 18 0 R
/Off 20 0 R
>>
endobj
Copy of object (11 0 obj) created with incremental update of PDF, included in saved file following user selection:
11 0 obj
<<
/C [ 0 ]
/FT /Btn
/F 4
/BS <<
/W 0
>>
/Subtype /Widget
/DA (//Helvetica 13 Tf 0 g)
/Type /Annot
/Border [ 0 0 0 ]
/M (D:20230803164805Z00'00')
/Rect [ 433 405 453 425 ]
/MK <<
/BG [ 0.75 ]
>>
/AP <<
/N <<
/Off 53 0 R
/Ted 57 0 R
>>
>>
/T (button2)
/AS /Ted
/Ff 32768
/V /Ted
>>
endobj
A bug report, describing as much, was submitted to Apple (FB9978281).
I've noticed that edit menu with Copy, Look Up, Translate options doesn't appear when I select some text in pdf file loaded in WKWebView. This issue is reproducible on the latest iOS 16 and 17 beta versions.
Additionally, these two WKUIDelegate methods are called for all types of documents except PDFs.
func webView(_ webView: WKWebView, willPresentEditMenuWithAnimator animator: UIEditMenuInteractionAnimating)
func webView(_ webView: WKWebView, willDismissEditMenuWithAnimator animator: UIEditMenuInteractionAnimating)
Is this a bug or is there a new WebKit/PDFKit API I could use to enable this menu?
Feedback ID: FB12759407
PDFKit’s characterIndex(at:) method consistently returns the incorrect character index on iOS 17. It seems to get worse the further down the page you get. This breaks critical functionality that my app relies on. Prior to iOS 17, it would be wrong sometimes but not as consistently.
This is the method in question:
https://developer.apple.com/documentation/pdfkit/pdfpage/1503775-characterindex
I've filed feedback FB12951475 with a sample project attached.
PDFKit's PDFPage.characterBounds(at: Int) is returning incorrect coordinates with iOS 17 beta 5 / Xcode 15 beta 6. Worked fine iOS 16 and earlier.
It breaks critical functionality that my app relies on.
I have filed feedback (FB12918701).
I use PDFView to show PDF files. A PDFView has several editmenu items in advance. Among them, I want to remove the highlight from the item. So, I made PDFView subclass and overrode canPerformAction and buildMenu respectively, but I could not remove the highlight item from the editmenu. If you know how to remove the highlight item, please let me know. Thanks for reading my post.
I have the following code in a prototype application that displays text with a link and presents it in a PDFView.
let a4SizePageRect = CGRect(x: 0, y: 0, width: 595.2, height: 841.8)
let renderer = UIGraphicsPDFRenderer(bounds: a4SizePageRect)
let title = "Some Title Link"
let attributedTitle = NSMutableAttributedString(string: title, attributes: [
.font: UIFont.boldSystemFont(ofSize: 24),
.link: "<insert here some correct link>",
.underlineStyle: NSUnderlineStyle.single.rawValue
])
let data = renderer.pdfData { ctx in
ctx.beginPage()
attributedTitle.draw(in: a4SizePageRect.insetBy(dx: 25, dy: 25))
}
pdfView.document = PDFDocument(data: data)
The link only works on iOS 15 versions, but does not work on iOS 16.4 and iOS 17 RC.
Based on the documentation for the property and NSAttributedString, the link attribute should still work.
Has NSAttributedString stopped supporting NSAttributedString.Key.link in recent versions of iOS?
Hi there,
I am currently facing an issue with PDFKit page rendering. In one of my application, I have to open annoted PDF for reading and when user tap on particular annotation, I have to jump certain number of pages in same PDF to open the new desired page.
Example: User is on page 2 and when user tap on menu button, I need to navigate directly on page number 8.
With above scenario, my application takes a while to load page number 8 and User can see white screen before loading PDF page. When I checked console log, I can see following as an error.
F.I: App requires to use usePageViewController for pagination.
I am using as per following:
_pdfView = [[PDFView alloc] initWithFrame:[UIScreen mainScreen].bounds];
[view addSubview:self.pdfView];
self.pdfView.delegate = self;
[self.pdfView setAutoScales:true];
[self.pdfView setDisplayDirection:kPDFDisplayDirectionHorizontal];
[self.pdfView usePageViewController:true withViewOptions:nil];
[self.pdfView setBackgroundColor:UIColor.whiteColor];
Warning: Unable to complete drawing page index 24 on time as a request to forceUpdateActivePageIndex:withMaxDuration: 0.02
I hope if someone can help me on that
Thanks.
Hello y'all,
I would like to discuss here if anyone else is noticing that some PDF files are not rendered as expected in iOS/iPadOS 17, it seems that some text with background (screenshot attached) are not rendered and you can only see the back color.
The issue is reproducible on Preview, Safari, where I guess Apple is using PDFKit framework too.
We submitted different issues with Feedback Assistant, however I've not hear back from Apple yet.
Is anyone else able to reproduce the issue?
Thanks,
I have a problem with the Notes app on ios17. When I open a pdf file that I created from scanning a document with my iPhone 14, I can't select any text from the pdf. This makes it impossible to copy and paste anything from my pdf. Is anyone else having this issue?