I'm developing a PDF application that display vertical pages horizontally. Some of users might use mouses with scroll wheels. I want to provide a option enabling them to browse pages with scroll wheels. How can I achieve that with PDFKit?
PDFKit
RSS for tagDisplay and manipulate PDF documents in your applications using PDFKit.
Posts under PDFKit tag
63 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hello together,
does anyone know how I can scale a view to a given document size (e.g. A4: 210/297 mm) without scaling the view itself?
With enclosed code I can create the pdf-document, but the paperize is not the intended size. It is too big.
Variation this line of code
var mediaBox = CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height))
into
var mediaBox = CGRect(origin: .zero, size: CGSize(width: 595, height: 842)
does not scale the view (it just shows a part of it at the correct papersize).
So what do I have to do to scale the rendered view to the proper size?
Enclosed a very simple code snipped to see th strange behavior.
Thx, best regards
Peter
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Rectangle()
.frame(width: 2100, height: 2910)
.foregroundColor(.red)
Rectangle()
.frame(width: 2100/2, height: 2910/3)
.foregroundColor(.blue)
Text("Hello, world!")
}
.padding()
.onAppear(perform: {
generatePDF()
})
}
// generate pdf from given view
@MainActor func generatePDF() -> URL {
// Select UI View to render as pdf
let image = ImageRenderer(content: ContentView())
let url = URL.documentsDirectory.appending(path: "generatedPDF.pdf")
image.render{ size, context in
var mediaBox = CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height))
guard let consumer = CGDataConsumer(url: url as CFURL),
let pdfContext = CGContext(consumer: consumer, mediaBox: &mediaBox, nil)
else {
return
}
pdfContext.beginPDFPage(nil)
pdfContext.translateBy(x: mediaBox.size.width / 2 - size.width / 2,
y: mediaBox.size.height / 2 - size.height / 2)
context(pdfContext)
pdfContext.endPDFPage()
pdfContext.closePDF()
}
print("Saving PDF to \(url.path())")
return url
}
}
#Preview {
ContentView()
}
![]("https://developer.apple.com/forums/content/attachment/67b11ec8-af9e-4ab7-a0a5-0c020b7a45d4" "title=generatedPDF (without scaling).jpg;width=2652;height=2526")
![]("https://developer.apple.com/forums/content/attachment/b2c24a25-3805-4664-8adb-dcb81c3e96c4" "title=generatedPDF (with scaling).jpg;width=2132;height=1510")
I’m showing a PDF page in UIView’s subview using PDFKit along with some UILabel and UIImageView. At a time I’m only showing one page in PDFView. Users can change the size and position of this PDFView.
class ResizablePDFView: PDFView {
override func draw(_ rect: CGRect) {
super.draw(rect)
}
override func draw(_ layer: CALayer, in ctx: CGContext) {
let isPDF = !UIGraphicsGetPDFContextBounds().isEmpty
if isPDF {
if let document = self.document, document.pageCount > 0, let page = document.page(at: 0) {
ctx.saveGState()
ctx.scaleBy(x: 1, y: -1)
ctx.translateBy(x: 0, y: -bounds.size.height)
let pageBounds = page.bounds(for: .mediaBox)
ctx.scaleBy( x: bounds.size.width / pageBounds.size.width, y: bounds.size.height / pageBounds.size.height)
ctx.translateBy(x: -pageBounds.origin.x, y: -pageBounds.origin.y)
page.draw(with: .mediaBox, to: ctx)
ctx.restoreGState()
}
}else {
super.draw(layer, in: ctx)
}
}
}
class ResizableLabelView: UILabel {
func setup() {
self.font = UIFont.systemFont(ofSize: 20)
self.textColor = UIColor.systemBlue
}
override func draw(_ rect: CGRect) {
super.draw(rect)
}
override func draw(_ layer: CALayer, in ctx: CGContext) {
let isPDF = !UIGraphicsGetPDFContextBounds().isEmpty
if isPDF {
draw(bounds)
}else {
super.draw(layer, in: ctx)
}
}
}
CanvasView setup,
class ViewController: UIViewController {
var canvasView: UIView!
var pdfView: ResizablePDFView!
var label: ResizableLabelView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.gray
self.canvasView = UIView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 400, height: 573)))
self.canvasView.center = self.view.center
self.canvasView.backgroundColor = UIColor.white
self.view.addSubview(self.canvasView)
self.setupPDF()
self.setupLabel()
}
func setupPDF() {
self.pdfView = ResizablePDFView(frame: CGRect(origin: .zero, size: self.canvasView.frame.size))
self.pdfView.backgroundColor = UIColor.clear
self.canvasView.addSubview(self.pdfView)
self.pdfView.autoScales = false
self.pdfView.displayMode = .singlePage
self.pdfView.displaysPageBreaks = false
self.pdfView.pageBreakMargins = UIEdgeInsets.zero
self.pdfView.pageShadowsEnabled = false
if let file = Bundle.main.url(forResource: "sample_pdf", withExtension: "pdf") {
if let pdfDocument = PDFDocument(url: file) {
let pageNumber: Int = 0
if let page = pdfDocument.page(at: pageNumber) {
let pageDocument = PDFDocument()
pageDocument.insert(page, at: 0)
self.pdfView.document = pageDocument
self.pdfView.minScaleFactor = self.pdfView.scaleFactorForSizeToFit
self.pdfView.maxScaleFactor = self.pdfView.scaleFactorForSizeToFit
self.pdfView.scaleFactor = self.pdfView.scaleFactorForSizeToFit
}
}
}
}
func setupLabel() {
self.label = ResizableLabelView(frame: CGRect(x: 10, y: 10, width: 200, height: 50))
self.label.setup()
self.label.text = "Sample Text"
self.label.sizeToFit()
self.canvasView.addSubview(self.label)
}
}
now, I'm creating the PDF from canvasView
@IBAction func exportButtonAction(_ sender: UIButton) {
let filePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Exported_PDF.pdf")
UIGraphicsBeginPDFContextToFile(filePath.path, .zero, [kCGPDFContextCreator as String: "PDF Export Demo App"])
guard let canvas = self.canvasView else { return }
UIGraphicsBeginPDFPageWithInfo(canvas.bounds, nil)
guard let context = UIGraphicsGetCurrentContext() else { return }
canvas.setNeedsDisplay()
canvas.layer.render(in: context)
UIGraphicsEndPDFContext()
print(filePath)
}
Now, This will render UILabel and UIImageView in PDF properly without rasterization and selectable text, but It does not draw PDFView like original pdf with links.
What am I doing wrong here? how can I debug this issue?
https://drive.google.com/drive/folders/1qLcGNGWxoXbWJnr6xBxueKjPHnfxYS6D?usp=drive_link
I'm looking for guidance on how to prepare a PDF for inserting a digital certificate on iOS without a commercial 3rd party tool.
The idea is to...
Set up a signature annotation with PDFKit,
Set up/prepare the PDF with the required information (contact info, adbe.pkcs7.detached, the Adobe.PPKLite filter , info/annotation reference, the UTC time, etc.)
Pad the content area with zeros (I believe it would 4096
Then compute the hash.
I am following Adobe's Spec (Acrobat_DigitalSignatures_in_PDF)
Would this be possible with parsing out and adding the data from a byte stream?
Can I simply build the signature object on my own and place them into the PDF, or are there any other items I need to change.
Are there any tools out there that can help me manipulate the data and/or get the document hash easily?
For example the data would look something like this is...
<</ByteRange [0 552215 560537 907] /ContactInfo (my contact info)/Contents <
AREA OF ZERO PADDING FOR CERT INSET)
00>/Filter /Adobe.PPKLite/M (D:20240223095734Z)/Reason (Some reason)/Reference [<</Data 13 0 R/TransformMethod/FieldMDP/TransformParams 39 0 R/Type/SigRef>>]/SubFilter /adbe.pkcs7.detached/Type /Sig>>
Thank you
I am trying to crop a pdf to remove 100 points from the top and bottom of the page.
The mediaBounds of my pdf is size 612x792 and the origin is 0,0.
My code the set the bounds of the cropBox is:
page.setBounds(CGRect(origin: CGPointMake(0,100), size: CGSize(width: 612, height: 592)), for: .cropBox)
This sets the origin at 0,100 and the cropBox size to be 200 less than the mediaBox size. This works on iPhone, but on iPad I need to set the height to be 692, otherwise too much is cropped. Is this a bug or is there an explanation?
I am trying to convert MSOffice files such as docx, Ppt and xlsx to PDF files, but I couldn't find any native open source libraries to do the same. Is there any framework or library in swift which can do this.?
Hello, I have a question about PDF data creation on iOS 17.
My app creates PDF data from HTML for printing, and it is not possible to print out the PDF data to a particular printer when the app runs on iOS 17, despite that it works fine on iOS 16 and earlier.
The cause of the printing problem is unclear, however, I found that the contents of PDF data are different between iOS 17 and iOS 16 in the following 4 points:
PDF version
iOS 17: Ver. 1.4
iOS 16: Ver. 1.3
Size of PDF data
iOS 17: 100KB
iOS 16: 505KB
Embedded font
iOS 17: HiraginoSans-W5
iOS 16: HiraginoSans-W3
Displayed text size
iOS 17: just a little bigger than 14pt
iOS 16: 14pt
I am hoping that the printing problem can be resolved if my app running on iOS 17 creates the same PDF data as on iOS 16, so my question is:
Is there any way to create the same format of PDF data as iOS 16 or earlier when an app is running on iOS 17, especially to create data in version 1.3 of PDF?
In my app, I use almost the same as the following code to create PDF data from HTML, and I couldn't find any option to specify a version of PDF for UIPrintPageRenderer, UIMarkupTextPrintFormatter and UIGraphicsPDFContext classes...
Any info would be appreciated. Thanks,
let htmlString = """
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=420,height=595, shrink-to-fit=yes" />
<style type="text/css">
* {
box-sizing: border-box;
font-family: 'Hiragino Mincho ProN', serif;
font-size: 1em;
line-height: 1;
}
body {
margin: 0;
padding: 0;
font-size: 14pt;
}
span.gothic {
font-size: 3.5em;
font-family: "Helvetica Neue", Helvetica, "Hiragino Sans", sans-serif;
}
</style>
</head>
<body>
明朝体のテキスト
<span class="gothic">ゴシック体のテキスト</span>
</body>
</html>
"""
let render = UIPrintPageRenderer()
let paperRect = CGRect(x: 0, y: 0, width: 420.0, height: 595) // A5, 72 dpi
render.setValue(paperRect, forKey: "paperRect")
let contentsRect = CGRect(x: 0, y: 0, width: 420.0, height: 595) // A5, 72 dpi
render.setValue(contentsRect, forKey: "printableRect")
let fmt = UIMarkupTextPrintFormatter(markupText: htmlString)
render.addPrintFormatter(fmt, startingAtPageAt: 0)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, paperRect, nil)
render.prepare(forDrawingPages: NSMakeRange(0, render.numberOfPages))
let bound = UIGraphicsGetPDFContextBounds()
for i in 0..<render.numberOfPages {
UIGraphicsBeginPDFPage()
render.drawPage(at: i, in: bound)
}
UIGraphicsEndPDFContext()
do {
let url = URL(fileURLWithPath:"test.pdf")
try pdfData.write(to: url)
print("Done")
} catch {
print("Failed to save", error)
}
Can we restrict the cursor from moving out of the Pdf widget textfield in a pdf document. While updating the textfield user can tap enter or tab buttons to move the cursor out of the textfield.
Is this an expected behavior of Pdf widget textfield or is there a way to prevent this?
The entered value in the text view of pdf is getting disappeared after clicking on the submit. We are not manipulating the entered value anywhere. This was working fine until 17.1 update. Please let me know if there is any solution for this issue.
I'm getting infrequent crashes when I try to show a newly created PDF. The PDF is file based, and shortly after UIGraphicsEndPDFContext its shown.
The crash reports make it appear that the file itself is being mutated after its displayed.
So my question: is the file (self.filePath) absolutely flushed and closed when UIGraphicsEndPDFContext returns?
If not, is there some way to detect when it has finished?
Thanks!
David
`func addPageNumbers() {
let url = URL(fileURLWithPath: filePath)
guard let document = CGPDFDocument(url as CFURL) else { return }
// You have to change the file path otherwise it can cause blank white pages to be drawn.
self.filePath = "\(filePath)-final.pdf"
UIGraphicsBeginPDFContextToFile(filePath, .zero, nil)
let pageCount = document.numberOfPages
for i in 1...pageCount {
...
}
}
UIGraphicsEndPDFContext()
}
Hi everyone,
I'm trying to make a custom highlight functionality in PDFKit's PDFView on iOS 16. I've tried everything recommended online, and it seems like there's no way to fully override the menu and remove the normal "Highlight" action which actually does nothing if you tap it.
I'd be okay with being able to either remove the base Highlight action because I'm able to add my own, or figure out how to handle a tap on that button. Here's what I've tried, and the result:
override func buildMenu(with builder: UIMenuBuilder) {
super.buildMenu(with: builder)
builder.remove(menu: .application)
builder.remove(menu: .lookup)
builder.remove(menu: .learn)
builder.remove(menu: .find)
builder.remove(menu: .file)
builder.remove(menu: .edit)
builder.remove(menu: .services)
if #available(iOS 16.0, *) {
builder.remove(menu: .document)
}
let highlightAction = UIAction(title: "Highlight 2", attributes: []) { _ in
print("This works")
}
}
let highlightMenu = UIMenu(title: "Highlight 2", options: [.displayInline], children: [highlightAction])
builder.remove(menu: .standardEdit)
builder.remove(menu: .share)
builder.remove(menu: .toolbar)
builder.remove(menu: .text)
builder.remove(menu: .font)
builder.remove(menu: .format)
builder.remove(menu: .transformations)
builder.remove(menu: .preferences)
builder.remove(menu: .window)
builder.remove(menu: .view)
builder.replaceChildren(ofMenu: .root, from: { _ in [highlightAction] })
}
Please help!
Hi,
I encounter various problems with inserting PKDrawing into a PDFAnnotation :
First : After "page.addAnnotation(myCustomAnnotation)", saved document seems corrupted (affected pages are displayed with a "X" watermark covering the whole page),
Second : The only way to extract PKDrawing from the annotation is unarchiveTopLevelObjectWithData: which is deprecated,
Final : I'm not able to re-read PKDrawings to restore PKCanvasView undoManager.
Does anyone have an idea on a correct way to do this?
Thank you in advance and happy new year everyone!
I am creating a PDF document with the information from the database.
For iOS I have it working completely, but for macOs I can't get it to work no matter what I try.
I now have this code:
func generateRentalPDF(_ invoiceModel: InvoiceModel, _ customerInfo: CustomerModel, _ completion: @escaping (Data?) -> Void) {
guard let baseDocument = loadBasePDF(), let page = baseDocument.page(at: 0) else {
completion(nil)
return
}
var pageBounds = page.bounds(for: .mediaBox)
let pdfData = NSMutableData()
guard let dataConsumer = CGDataConsumer(data: pdfData as CFMutableData),
let pdfContext = CGContext(consumer: dataConsumer, mediaBox: &pageBounds, nil) else {
completion(nil)
return
}
pdfContext.beginPDFPage(nil)
if let cgPage = page.pageRef {
pdfContext.drawPDFPage(cgPage)
}
// Set text attributes
let textString = "Invoice Number: \(invoiceModel.invoiceNumber)"
let textStyle = NSMutableParagraphStyle()
textStyle.alignment = .left
let textAttributes: [NSAttributedString.Key: Any] = [
.font: NSFont.systemFont(orSize: 18),
.foregroundColor: NSColor.black,
.paragraphStyle: textStyle
]
// Calculate text position
let textRect = CGRect(x: 100, y: pageBounds.height - 150, width: 300, height: 50)
// Draw the text
pdfContext.saveGState()
pdfContext.translateBy(x: 0, y: pageBounds.height)
pdfContext.scaleBy(x: 1.0, y: -1.0)
let attributedText = NSAttributedString(string: textString, attributes: textAttributes)
attributedText.draw(in: textRect)
pdfContext.restoreGState()
pdfContext.endPDFPage()
pdfContext.closePDF()
completion(pdfData as Data)
}
When I open the document in the app, I do see the base document. Even if I put a stroke around the text, I also see the stroke. However, the text does not appear on the screen.
What am I overlooking or what am I doing wrong?
I hope someone here has an answer for me, thank you very much in advance.
Dear Developer Community,
My app is saving handwritten notes, forms and images as annotations in PDF documents by using PDFKit.
Since iPadOS 17.2, the content of the annotations within the annotation boundaries is scaled down when saving the annotated PDF file. I.e. the annotation boundaries remain unchanged, but the displayed annotation content shrinks and no longer fills the boundaries. This gets worse with every save operation and applies both to newly created annotations and to elements that were saved before iPadOS 17.2.
This issue only occurred after updating to iPadOS 17.2. The same code on my test device with iPadOS 17.1 works perfectly.
Does anybody have a similar issue and/or found a workaround to solve this problem?
Thanks for any idea!
The following situation is given:
In your code you have a PDFDocument object. This contains a PDF file with form fields that are filled with text.
If you call the dataRepresentation() method on this PDFDocument object, you get back a data object:
let myDocumentData: Data = pdfDocument.dataRepresentation()!
If you now want to initialize a new PDFDocument object with this data object, the contents of the form fields are duplicated within the PDF.
let newPDF: PDFDocument = PDFDocument(data: myDocumentData)
If you now want to print the newPDF PDFDocument object by creating a new print job, you will get the following result:
What you actually expect to see:
You only see this behavior when you want to print or share the PDF. You won't see this behaviour inside a PDF View in your application.
This behaviour only appears since iOS/iPadOS 17.2
Steps to reproduce:
Get a PDF file with form fields, especially text fields
fill out these text fields with text
create a PDFDocument object with this PDF file
call the dataRepresentation() method on this PDFDocument object and store the result in a new variable
create a new PDFDocument object with the data object created in the previous step:PDFDocument(data: <data>)
Print the new created PDFDocument within iOS/iPadOS 17.2 or share it for example via email
I hope Apple will fix this bug soon!
We have an app that exports PDFs with a custom page size, using PSDKit. In iOS16 the PDF export would have the correct page size dimensions, but now iOS17 exports everything to a Letter (8.5x11) size, regardless of what the PDF size specs are defined in the code:
let pageWidth: CGFloat = 86.0 / 25.4 * 72
let pageHeight: CGFloat = 54.0 / 25.4 * 72
let pageSize = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
Any thoughts as to how to fix this?
I am trying to generate a PDF file with certain components draw with Spot Colours. Spot colours are used for printing and I am not clear on how one would do that but I think that if I can create a custom ColorSpace with a specific name or a color that has a specific name - our printer looks for the name Spot1 and they use the colour green.
Can anyone shed any light on how I might be able to do this. For reference I have attached two pdf files with two different spot colours in them.
I need to be able to create similar using CGContext and CGPDFDocument. I can already generate the PDF documents using CMYK colors but don't know how I can create the equivalent "spot" colors.
At the moment I am loading the page from these attached pdf files and scaling them to fill the page to get a background with the spot color. This works fine but I also need to generate text and lines using this same spot color and I am not clear how I could do that using the Core Graphics APIs.
My guess is I need to create a custom ColorSpace with a single color and then use that color for drawing with.
The only 'custom' option for creating a ColorSpace seems to be the CGColorSpace(propertyListPList:) constructor, however there does not appear to be any documentation on what needs to be in the property list to do so. Nor can I find any examples of that.
Any pointers would be appreciated.
Regards
On macOS page(for: viewPoint, nearest: false) returns the wrong page.
If I enter a coordinate on page 1 less than 1/3 of the way down, it returns that it is on page 0. Likewise on page 2 it will say page 1 anywhere in the top third.
Also PDFView's convert(point, to: page) will return seemingly random values for the document point's y value, but in the expected range of 0 to about 792 (for an 11" page at 72dpi). Displaying the document horizontal/vertical doesn't matter.
The x value of the point is always correct and everything works correctly on iOS.
I've tried Xcode 15.0 and 15.1 beta 3.
Anyone using these functions correctly, or seeing the same issue?
I am trying to set the top anchor point of a pdf that is inside of a view with the .ignoresSafeArea() modifier. I would also like it to work on the edges when the phone is in landscape although for simplicity I will only explain what I want for the top. I want it to function like the iOS Files app pdf viewer where when tapped it hides the navigation bars but the top of the pdf stays at the same place, but when you zoom in on the pdf it can fill the whole screen. When you zoom back out the top should return to the same place as before. Here is a simple view to show how it is being used:
@MainActor
struct ContentView: View {
@State var showBars: Bool = true
@State var pdfUrl: URL?
var body: some View {
NavigationStack {
GeometryReader { geo in
ScrollView {
TabView {
if let url = pdfUrl {
PDFViewer(pdfUrl: url)
.onTapGesture {
withAnimation {
showBars.toggle()
}
}
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
.frame(width: geo.size.width, height: geo.size.height)
}
.scrollDisabled(true)
}
.ignoresSafeArea(edges: !showBars ? .all : [])
}
.task {
pdfUrl = renderPDF()
}
}
func renderPDF() -> URL {
let renderer = ImageRenderer(content: VStack {})
let url = URL.documentsDirectory.appending(path: "samplepdf.pdf")
renderer.render { size, context in
guard let pdf = CGContext(url as CFURL, mediaBox: nil, nil) else {
return
}
pdf.beginPDFPage(nil)
context(pdf)
pdf.endPDFPage()
pdf.beginPDFPage(nil)
context(pdf)
pdf.endPDFPage()
pdf.closePDF()
}
return url
}
}
And here is what my pdfView looks like so far:
struct PDFViewer: View {
var pdfUrl: URL
var body: some View {
PDFSheetView(document: .init(url: pdfUrl))
}
}
class PDFViewController: UIViewController {
let document: PDFDocument?
var pdfView: PDFView!
init(document: PDFDocument?) {
self.document = document
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
let view = PDFView()
self.view = view
self.pdfView = view
view.document = document
view.displayDirection = .vertical
view.autoScales = true
view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
}
required init?(coder: NSCoder) {
document = nil
super.init(coder: coder)
return nil
}
override func viewDidLayoutSubviews() {
let bounds = view.bounds
if let document {
if let page = document.page(at: 0) {
let pageBounds = page.bounds(for: .mediaBox)
if bounds.width > 0 && pageBounds.width > 0 {
let scaleFactor = bounds.width / pageBounds.width
let subtractionFactor = scaleFactor * 0.0125
pdfView.minScaleFactor = scaleFactor - subtractionFactor
}
}
}
}
}
struct PDFSheetView: UIViewControllerRepresentable {
typealias UIViewControllerType = PDFViewController
let document: PDFDocument?
func makeUIViewController(context: Context) -> PDFViewController {
let controller = PDFViewController(document: document)
return controller
}
func updateUIViewController(_ uiViewController: PDFViewController, context: Context) {
}
}
Is this possible to do? Like I said before, I want it to function just like the iOS Files app pdf viewer.
How to fix it?
Errors:
"Cannot find type 'UIViewRepresentable' in scope"
"Cannot find type 'Context' in scope"
"Cannot find type 'Context' in scope"
I tried:
Re-installed the Xcode
Re-started computer
Great thanks.
import SwiftUI
import PDFKit
struct PDFViewerView: UIViewRepresentable {
var url: URL
func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: self.url)
return pdfView
}
func updateUIView(_ uiView: PDFView, context: Context) {
// Update the view.
}
}
Xcode Version 15.0.1 (15A507)