本文記錄PDF文件手繪簽名并重新保存;
主要實現(xiàn)步驟:
- PDF文件分頁加載
- 在PDF視圖上手繪涂鴉簽名
- 將繪制過的PDF頁和簽名一起截圖,并轉(zhuǎn)換為單頁PDF
4.將簽過名的單頁PDF合并至原始PDF文件
5.重新生成完整的PDF文件
一.分頁加載PDF文件:
該部分參考:iOS開發(fā)——PDF文件的加載及顯示(網(wǎng)絡(luò)、本地),雖然排版亂了寫,但是注釋是相當?shù)脑敱M,在此不做贅述;
1.加載PDF的方式:
- 網(wǎng)絡(luò)加載:
CGPDFDocumentRef pdfRefByDataByUrl(NSString *fileName) {
NSURL*url = [NSURL URLWithString:fileName];
CFURLRef refURL = (__bridge_retained CFURLRef)url;
CGPDFDocumentRef document =CGPDFDocumentCreateWithURL(refURL);
// CGDataProviderRelease(document);
CFRelease(refURL);
return document;
}
- 本地加載:
CGPDFDocumentRef pdfRefByFilePath(NSString *aFilePath) {
CFStringRef path;
CFURLRef url;
CGPDFDocumentRef document;
path = CFStringCreateWithCString(NULL, [aFilePath UTF8String], kCFStringEncodingUTF8);
url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, NO);
document = CGPDFDocumentCreateWithURL(url);
CFRelease(path);
CFRelease(url);
return document;
}
Demo中使用網(wǎng)絡(luò)加載:
//通過pdfRefByDataByUrl函數(shù)獲取PDF文件資源,
self->docRef = pdfRefByDataByUrl(@"http://teaching.csse.uwa.edu.au/units/CITS4401/practicals/James1_files/SPMP1.pdf");
2.將PDF文件分解為單頁PDF數(shù)據(jù),以便分頁展示:
#pragma mark - 獲取所有需要顯示的PDF所有頁面數(shù)據(jù)
- (void)getDataArrayValue {
//獲取總頁數(shù)(注意,該頁數(shù)從1開始)
size_t totalPages = CGPDFDocumentGetNumberOfPages(self->docRef);
self.totalPage = (int)totalPages;
for(int i = 1; i <= totalPages; i++) {
CGPDFPageRef pageRef = CGPDFDocumentGetPage(self->docRef,i);
CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFMediaBox);
// CGPDFPageRef 轉(zhuǎn) NSValue
NSValue *pageValue = [NSValue valueWithBytes:&pageRef objCType:@encode(CGPDFPageRef)];
// 保存PDF文件所有單頁的數(shù)據(jù)
[self.pages addObject:pageValue];
RiderPDFView *view = [[RiderPDFView alloc]initWithFrame:CGRectMake(0,0,pageRect.size.width,self.view.frame.size.height) documentRef: self->docRef andPageNum:i];
// 保存PDF文件所有單頁視圖
[self.dataArray addObject:view];
}
}
此處處理一次性將PDF所有頁面數(shù)據(jù)分頁加載,并保存在self.dataArray當中,實際中可以分批次(類似與上拉加載更多的邏輯)甚至是一頁一頁加載,視實際需求而定;
代碼段中為在數(shù)組中保存pageRef結(jié)構(gòu)體,將之轉(zhuǎn)換為NSValue對象;
二. 在PDF視圖上手繪涂鴉簽名
簽名涂鴉,主要實現(xiàn)參考:PPSSignatureView,可控制筆畫書寫厚度,更貼近鋼筆字的字跡;當然,自己畫也可以;
PPSSignatureView繼承與GLKView(GLKit框架),這里給出一個使用實例,不做過多描述:
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
_signatureView = [[PPSSignatureView alloc] initWithFrame:CGRectMake(10, 80, 300, 300) context:context];
_signatureView.strokeColor = [UIColor redColor];
GLKViewController *glkView = [[GLKViewController alloc] init];
glkView.view = _signatureView;
[self addChildViewController:glkView];
實現(xiàn)在PDF頁面(currentRiderPDFView)涂鴉,將[currentRiderPDFView addSubview:_signatureView]即可;(currentRiderPDFView 為第一步中分頁顯示PDF的RiderPDFView);
三.將繪制過的PDF頁和簽名一起截圖,并轉(zhuǎn)換為單頁PDF
1.將繪制過的PDF頁(RiderPDFView)和簽名圖層(PPSSignatureView)一起截圖:
因 PPSSignatureView 是加在 RiderPDFView之上,故,僅僅對 RiderPDFView 截圖即可獲取完整的繪制過的PDF圖像,實現(xiàn)如下:
UIImage *signatureImage = [self snapViewToImage:currentRiderPDFView];
-(UIImage *)snapViewToImage:(UIView *)view{
CGSize size = view.bounds.size;
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
} else {
[view.layer renderInContext:context];
}
CGContextRestoreGState(context);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
2.將UIIMage轉(zhuǎn)換為單頁PDF:
將上述獲得的 signatureImage轉(zhuǎn)換為單頁PDF,為下面合并至原始PDF做準備:
CGPDFPageRef currentSinglePageRef = [self imageToPDFPageRef:signatureImage];
-(CGPDFPageRef)imageToPDFPageRef:(UIImage *)image{
[PDFManager createPDFFileWithSrc:UIImageJPEGRepresentation(image, 1.0) toDestFile:@"signaturePage.pdf" withPassword:nil];
NSString *path = [PDFManager pdfDestPath:@"signaturePage.pdf"];
CGPDFDocumentRef newPDFRef = pdfRefByFilePath(path);
// size_t pageSize = CGPDFDocumentGetNumberOfPages(newPDFRef);
CGPDFPageRef pageRef = CGPDFDocumentGetPage(newPDFRef, 1);
NSLog(@"繪制的PDF頁:%@",path);
return pageRef;
}
其中 PDFManager主要實現(xiàn) UIIMage轉(zhuǎn)PDF并存儲,網(wǎng)上能找到的轉(zhuǎn)換實發(fā)基本就這么一個;
四.將簽過名的單頁PDF合并至原始PDF文件
到此就很明了了,上一步獲取繪制過的單頁PDF(currentSinglePageRef),將 currentSinglePageRef替換數(shù)組_pages對應位置的CGPDFPageRef實現(xiàn)PDF數(shù)據(jù)重新拼接:
CGPDFPageRef currentSinglePageRef = [self imageToPDFPageRef:signatureImage];
// 將 CGPDFPageRef 轉(zhuǎn)換成 NSValue 以便于在集合中存儲
NSValue *currentPageValue = [NSValue value:& currentSinglePageRef withObjCType:@encode(CGPDFPageRef)];
// 執(zhí)行替換
[self.pages replaceObjectAtIndex:self->currentPage withObject:currentPageValue];
五.重新生成完整的PDF文件:
講過上一步的處理,此時已經(jīng)獲得了涂鴉繪制過的完整PDF數(shù)據(jù)(_pages),接下來要做的就是將_pages重新生成PDF即可:
#pragma mark - 重新生成PDF文件,返回新文件地址
-(NSString *)reSavePDF:(NSArray <NSValue *> *)pdfPages{
// 新生成PDF文件路徑
NSString *fileName = [NSString stringWithFormat:@"公文%d.pdf",arc4random_uniform(100)];
NSString *pdfPathOutput = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:fileName];
CFURLRef pdfURLOutput = (CFURLRef)CFBridgingRetain([NSURL fileURLWithPath:pdfPathOutput]);
// Create the output context
CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL);
CGRect mediaBox;
CGPDFPageRef tempref;
// 寫入文件
for (int i = 0; i < pdfPages.count; i++) {
[[pdfPages objectAtIndex:i] getValue:&tempref];
mediaBox = CGPDFPageGetBoxRect(tempref, kCGPDFMediaBox);
CGContextBeginPage(writeContext, &mediaBox);
CGContextDrawPDFPage(writeContext, tempref);
CGContextEndPage(writeContext);
}
CFRelease(pdfURLOutput);
CGPDFContextClose(writeContext);
CGContextRelease(writeContext);
return pdfPathOutput;
}
至此,全新的PDF文件也就生成完成;
若服務端能處理的話,只上傳修改過的單頁是最佳策略 ....
加密 .... 上傳 .... 清沙盒 .... anything you want ....