iOS中是否有一种方法可以合并PDF文件,即将一个PDF文件的页面附加在另一个PDF文件的末尾并将其保存到磁盘上?
iOS中是否有一种方法可以合并PDF文件,即将一个PDF文件的页面附加在另一个PDF文件的末尾并将其保存到磁盘上?
我对Jonathan的代码进行了一些小重构,以合并任何大小的PDF文件:
+ (NSString *)joinPDF:(NSArray *)listOfPaths {
// File paths
NSString *fileName = @"ALL.pdf";
NSString *pdfPathOutput = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:fileName];
CFURLRef pdfURLOutput = ( CFURLRef)CFBridgingRetain([NSURL fileURLWithPath:pdfPathOutput]);
NSInteger numberOfPages = 0;
// Create the output context
CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL);
for (NSString *source in listOfPaths) {
CFURLRef pdfURL = ( CFURLRef)CFBridgingRetain([[NSURL alloc] initFileURLWithPath:source]);
//file ref
CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL);
numberOfPages = CGPDFDocumentGetNumberOfPages(pdfRef);
// Loop variables
CGPDFPageRef page;
CGRect mediaBox;
// Read the first PDF and generate the output pages
DLog(@"GENERATING PAGES FROM PDF 1 (%@)...", source);
for (int i=1; i<=numberOfPages; i++) {
page = CGPDFDocumentGetPage(pdfRef, i);
mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGContextBeginPage(writeContext, &mediaBox);
CGContextDrawPDFPage(writeContext, page);
CGContextEndPage(writeContext);
}
CGPDFDocumentRelease(pdfRef);
CFRelease(pdfURL);
}
CFRelease(pdfURLOutput);
// Finalize the output file
CGPDFContextClose(writeContext);
CGContextRelease(writeContext);
return pdfPathOutput;
}
希望这有所帮助。我想出了这个解决方案:
// Documents dir
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// File paths
NSString *pdfPath1 = [documentsDirectory stringByAppendingPathComponent:@"1.pdf"];
NSString *pdfPath2 = [documentsDirectory stringByAppendingPathComponent:@"2.pdf"];
NSString *pdfPathOutput = [documentsDirectory stringByAppendingPathComponent:@"out.pdf"];
// File URLs
CFURLRef pdfURL1 = (CFURLRef)[[NSURL alloc] initFileURLWithPath:pdfPath1];
CFURLRef pdfURL2 = (CFURLRef)[[NSURL alloc] initFileURLWithPath:pdfPath2];
CFURLRef pdfURLOutput = (CFURLRef)[[NSURL alloc] initFileURLWithPath:pdfPathOutput];
// File references
CGPDFDocumentRef pdfRef1 = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL1);
CGPDFDocumentRef pdfRef2 = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL2);
// Number of pages
NSInteger numberOfPages1 = CGPDFDocumentGetNumberOfPages(pdfRef1);
NSInteger numberOfPages2 = CGPDFDocumentGetNumberOfPages(pdfRef2);
// Create the output context
CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL);
// Loop variables
CGPDFPageRef page;
CGRect mediaBox;
// Read the first PDF and generate the output pages
NSLog(@"GENERATING PAGES FROM PDF 1 (%i)...", numberOfPages1);
for (int i=1; i<=numberOfPages1; i++) {
page = CGPDFDocumentGetPage(pdfRef1, i);
mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGContextBeginPage(writeContext, &mediaBox);
CGContextDrawPDFPage(writeContext, page);
CGContextEndPage(writeContext);
}
// Read the second PDF and generate the output pages
NSLog(@"GENERATING PAGES FROM PDF 2 (%i)...", numberOfPages2);
for (int i=1; i<=numberOfPages2; i++) {
page = CGPDFDocumentGetPage(pdfRef2, i);
mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGContextBeginPage(writeContext, &mediaBox);
CGContextDrawPDFPage(writeContext, page);
CGContextEndPage(writeContext);
}
NSLog(@"DONE!");
// Finalize the output file
CGPDFContextClose(writeContext);
// Release from memory
CFRelease(pdfURL1);
CFRelease(pdfURL2);
CFRelease(pdfURLOutput);
CGPDFDocumentRelease(pdfRef1);
CGPDFDocumentRelease(pdfRef2);
CGContextRelease(writeContext);
这里最大的问题是内存分配。如你所见,在这种方法中,你必须同时读取要合并的两个PDF文件,并生成输出。释放内存只在最后发生。我尝试将一个包含500页(约15MB)的PDF文件与另一个包含100页(约3MB)的文件合并,它产生了一个新的拥有600页(当然!)仅大小约为5MB(神奇?)。执行时间约为30秒(考虑到iPad 1还好),分配了17MB的内存(痛苦!)。应用程序幸运地没有崩溃,但我认为iOS会乐意杀死像这样消耗17MB内存的应用程序。;P
CGPDFContextClose
关闭上下文(将文件保存到磁盘)后,无法重新打开并继续编辑,就像将指针移动到文件末尾并添加新内容一样。 - JonatanCGPDFPageGetBoxRect(page, kCGPDFMediaBox)
,但参数 kCGPDFMediaBox
可能并不总是最佳选择。在一个用于印刷杂志的文件中,前后封面印在同一张纸上,使用 kCGPDFMediaBox
会使我在同一页上得到两个封面,而 kCGPDFCropBox
则可以正确地裁剪可见页面。 - SaltyNuts我的Swift 3函数:
// sourcePdfFiles is array of source file full paths, destPdfFile is dest file full path
func mergePdfFiles(sourcePdfFiles:[String], destPdfFile:String) {
guard UIGraphicsBeginPDFContextToFile(destPdfFile, CGRect.zero, nil) else {
return
}
guard let destContext = UIGraphicsGetCurrentContext() else {
return
}
for index in 0 ..< sourcePdfFiles.count {
let pdfFile = sourcePdfFiles[index]
let pdfUrl = NSURL(fileURLWithPath: pdfFile)
guard let pdfRef = CGPDFDocument(pdfUrl) else {
continue
}
for i in 1 ... pdfRef.numberOfPages {
if let page = pdfRef.page(at: i) {
var mediaBox = page.getBoxRect(.mediaBox)
destContext.beginPage(mediaBox: &mediaBox)
destContext.drawPDFPage(page)
destContext.endPage()
}
}
}
destContext.closePDF()
UIGraphicsEndPDFContext()
}
我想分享一下使用Swift的答案,因为我在查找Swift时没有找到它,不得不进行翻译。此外,我的答案使用了一个名为pdfPagesURLArray
的单独pdf数组,并循环生成完整的pdf。我还是个新手,欢迎任何建议。
let file = "fileName.pdf"
guard var documentPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first else {
NSLog("Doh - can't find that path")
return
}
documentPaths = documentPaths.stringByAppendingString(file)
print(documentPaths)
let fullPDFOutput: CFURLRef = NSURL(fileURLWithPath: documentPaths)
let writeContext = CGPDFContextCreateWithURL(fullPDFOutput, nil, nil)
for pdfURL in pdfPagesURLArray {
let pdfPath: CFURLRef = NSURL(fileURLWithPath: pdfURL)
let pdfReference = CGPDFDocumentCreateWithURL(pdfPath)
let numberOfPages = CGPDFDocumentGetNumberOfPages(pdfReference)
var page: CGPDFPageRef
var mediaBox: CGRect
for index in 1...numberOfPages {
你可以像这样强制解包:
page = CGPDFDocumentGetPage(pdfReference, index)!
但是为了遵循最佳实践,建议采用以下方式:
guard let getCGPDFPage = CGPDFDocumentGetPage(pdfReference, index) else {
NSLog("Error occurred in creating page")
return
}
page = getCGPDFPage
mediaBox = CGPDFPageGetBoxRect(page, .MediaBox)
CGContextBeginPage(writeContext, &mediaBox)
CGContextDrawPDFPage(writeContext, page)
CGContextEndPage(writeContext)
}
}
NSLog("DONE!")
CGPDFContextClose(writeContext);
NSLog(documentPaths)
Swift 5:
import PDFKit
func mergePdf(data: Data, otherPdfDocumentData: Data) -> PDFDocument {
// get the pdfData
let pdfDocument = PDFDocument(data: data)!
let otherPdfDocument = PDFDocument(data: otherPdfDocumentData)!
// create new PDFDocument
let newPdfDocument = PDFDocument()
// insert all pages of first document
for p in 0..<pdfDocument.pageCount {
let page = pdfDocument.page(at: p)!
let copiedPage = page.copy() as! PDFPage // from docs
newPdfDocument.insert(copiedPage, at: newPdfDocument.pageCount)
}
// insert all pages of other document
for q in 0..<otherPdfDocument.pageCount {
let page = pdfDocument.page(at: q)!
let copiedPage = page.copy() as! PDFPage
newPdfDocument.insert(copiedPage, at: newPdfDocument.pageCount)
}
return newPdfDocument
}
PDF文档的插入功能可以在文档中找到,文档中写道:
open class PDFDocument : NSObject, NSCopying {
...
// Methods allowing pages to be inserted, removed, and re-ordered. Can throw range exceptions.
// Note: when inserting a PDFPage, you have to be careful if that page came from another PDFDocument. PDFPage's have a
// notion of a single document that owns them and when you call the methods below the PDFPage passed in is assigned a
// new owning document. You'll want to call -[PDFPage copy] first then and pass this copy to the blow methods. This
// allows the orignal PDFPage to maintain its original document.
open func insert(_ page: PDFPage, at index: Int)
open func removePage(at index: Int)
open func exchangePage(at indexA: Int, withPageAt indexB: Int)
...
}
在您的类中创建 mergedPdf
变量:
var mergedPdf: PDFDocument?
viewDidLoad()
调用合并函数并显示合并后的PDF文件: mergedPdf = mergePdf(data: pdfData1, otherPdfDocumentData: pdfData2)
// show merged pdf in pdfView
PDFView.document = mergedPdf!
首先按照以下步骤将新PDF文件转换:
let documentDataForSaving = mergedPdf.dataRepresentation()
将documentDataForSaving
放入以下函数中:
使用保存函数:
let urlWhereTheFileIsSaved = writeDataToTemporaryDirectory(withFilename: "My File Name", inFolder: nil, data: documentDataForSaving)
也许您希望在文件名中避免使用/
,并且不要使其超过256个字符的长度。
保存功能:
// Export PDF to directory, e.g. here for sharing
func writeDataToTemporaryDirectory(withFilename: String, inFolder: String?, data: Data) -> URL? {
do {
// get a directory
var temporaryDirectory = FileManager.default.temporaryDirectory // for e.g. sharing
// FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last! // to make it public in user's directory (update plist for user access)
// FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).last! // to hide it from any user interactions)
// do you want to create subfolder?
if let inFolder = inFolder {
temporaryDirectory = temporaryDirectory.appendingPathComponent(inFolder)
if !FileManager.default.fileExists(atPath: temporaryDirectory.absoluteString) {
do {
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription);
}
}
}
// name the file
let temporaryFileURL = temporaryDirectory.appendingPathComponent(withFilename)
print("writeDataToTemporaryDirectory at url:\t\(temporaryFileURL)")
try data.write(to: temporaryFileURL)
return temporaryFileURL
} catch {
print(error)
}
return nil
}
PDFDocument
上的insert
方法期望一个类型为PDFPage
的对象,而不是另一个PDFDocument
。你是否碰巧在Xcode 13.3中尝试过这个? - Clifton Labruminsert
是PDFDocument
的一个函数,需要一个PDFPage
,我复制并粘贴了我的代码,现在它可以完美运行了,你还有什么问题吗? - FrugalResolution+ (void)combinePDFURLs:(NSArray *)PDFURLs writeToURL:(NSURL *)URL
{
CGContextRef context = CGPDFContextCreateWithURL((__bridge CFURLRef)URL, NULL, NULL);
for (NSURL *PDFURL in PDFURLs) {
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL((__bridge CFURLRef)PDFURL);
size_t numberOfPages = CGPDFDocumentGetNumberOfPages(document);
for (size_t pageNumber = 1; pageNumber <= numberOfPages; ++pageNumber) {
CGPDFPageRef page = CGPDFDocumentGetPage(document, pageNumber);
CGRect mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGContextBeginPage(context, &mediaBox);
CGContextDrawPDFPage(context, page);
CGContextEndPage(context);
}
CGPDFDocumentRelease(document);
}
CGPDFContextClose(context);
CGContextRelease(context);
}