iOS PDF之旅(一)创建PDF文件

最近要写关于PDF读取和涂鸦编辑的应用,这段时间在看PDF的相关资料,顺便写下Demo做下短篇笔记。

 

直接上代码吧:

 

  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.     // 1.创建media box  
  6.     CGFloat myPageWidth = self.view.bounds.size.width;  
  7.     CGFloat myPageHeight = self.view.bounds.size.height;  
  8.     CGRect mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);  
  9.       
  10.       
  11.     // 2.设置pdf文档存储的路径  
  12.     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
  13.     NSString *documentsDirectory = paths[0];  
  14.     NSString *filePath = [documentsDirectory stringByAppendingString:@"/test.pdf"];  
  15.     // NSLog(@"%@", filePath);  
  16.     const charchar *cfilePath = [filePath UTF8String];  
  17.     CFStringRef pathRef = CFStringCreateWithCString(NULL, cfilePath, kCFStringEncodingUTF8);  
  18.       
  19.       
  20.     // 3.设置当前pdf页面的属性  
  21.     CFStringRef myKeys[3];  
  22.     CFTypeRef myValues[3];  
  23.     myKeys[0] = kCGPDFContextMediaBox;  
  24.     myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));  
  25.     myKeys[1] = kCGPDFContextTitle;  
  26.     myValues[1] = CFSTR("我的PDF");  
  27.     myKeys[2] = kCGPDFContextCreator;  
  28.     myValues[2] = CFSTR("Creator Name");  
  29.     CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const voidvoid **) myKeys, (const voidvoid **) myValues, 3,  
  30.                                                         &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);  
  31.       
  32.       
  33.     // 4.获取pdf绘图上下文  
  34.     CGContextRef myPDFContext = MyPDFContextCreate (&mediaBox, pathRef);  
  35.       
  36.       
  37.     // 5.开始描绘第一页页面  
  38.     CGPDFContextBeginPage(myPDFContext, pageDictionary);  
  39.     CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);  
  40.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));  
  41.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);  
  42.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));  
  43.       
  44.     // 为一个矩形设置URL链接www.baidu.com  
  45.     CFURLRef baiduURL = CFURLCreateWithString(NULL, CFSTR("http://www.baidu.com"), NULL);  
  46.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  47.     CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));  
  48.     CGPDFContextSetURLForRect(myPDFContext, baiduURL, CGRectMake (200, 200, 100, 200 ));  
  49.       
  50.     CGPDFContextEndPage(myPDFContext);  
  51.       
  52.       
  53.       
  54.     // 6.开始描绘第二页页面  
  55.     // 注意要另外创建一个page dictionary  
  56.     CFDictionaryRef page2Dictionary = CFDictionaryCreate(NULL, (const voidvoid **) myKeys, (const voidvoid **) myValues, 3,  
  57.                                                         &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);  
  58.     CGPDFContextBeginPage(myPDFContext, page2Dictionary);  
  59.       
  60.     // 在左下角画两个矩形  
  61.     CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);  
  62.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));  
  63.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);  
  64.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));  
  65.       
  66.     // 在右下角写一段文字:"Hello world"  
  67.     CGContextSelectFont(myPDFContext, "Helvetica", 30, kCGEncodingMacRoman);  
  68.     CGContextSetTextDrawingMode (myPDFContext, kCGTextFill);  
  69.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  70.     const charchar *text = [@"Hello world" UTF8String];  
  71.     CGContextShowTextAtPoint (myPDFContext, 120, 120, text, strlen(text));  
  72.       
  73.     /* 
  74.     // 为某一个矩形设置destination,这里destination的作用还不是很明白,保留 
  75.     CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("Hello world"), CGRectMake(50.0, 300.0, 100.0, 100.0)); 
  76.     CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5); 
  77.     CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0)); 
  78.      */  
  79.       
  80.     // 为右上角的矩形设置一段file URL链接,打开本地文件  
  81.     NSURL *furl = [NSURL fileURLWithPath:@"/Users/one/Library/Application Support/iPhone Simulator/7.0/Applications/3E7CB341-693A-4FE4-8FE5-A827A5210F0A/Documents/test1.pdf"];  
  82.     CFURLRef fileURL = (__bridge CFURLRef)furl;  
  83.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  84.     CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));  
  85.     CGPDFContextSetURLForRect(myPDFContext, fileURL, CGRectMake (200, 200, 100, 200 ));  
  86.       
  87.     CGPDFContextEndPage(myPDFContext);  
  88.       
  89.       
  90.       
  91.     // 7.释放创建的对象  
  92.     CFRelease(page2Dictionary);  
  93.     CFRelease(pageDictionary);  
  94.     CFRelease(myValues[0]);  
  95.     CGContextRelease(myPDFContext);  
  96. }  
  97.   
  98. /* 
  99.  * 获取pdf绘图上下文 
  100.  * inMediaBox指定pdf页面大小 
  101.  * path指定pdf文件保存的路径 
  102.  */  
  103. CGContextRef MyPDFContextCreate (const CGRect *inMediaBox, CFStringRef path)  
  104. {  
  105.     CGContextRef myOutContext = NULL;  
  106.     CFURLRef url;  
  107.     CGDataConsumerRef dataConsumer;  
  108.       
  109.     url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, false);  
  110.       
  111.     if (url != NULL)  
  112.     {  
  113.         dataConsumer = CGDataConsumerCreateWithURL(url);  
  114.         if (dataConsumer != NULL)  
  115.         {  
  116.             myOutContext = CGPDFContextCreate (dataConsumer, inMediaBox, NULL);  
  117.             CGDataConsumerRelease (dataConsumer);  
  118.         }  
  119.         CFRelease(url);  
  120.     }  
  121.     return myOutContext;  
  122. }  


代码注释基本说明了所有问题,这里要注意的就是如果要创建多页的pdf文件,就必须设置多个page dictionary从而获取不同的pdf绘图上下文。

 

 

打开文件保存的路径,可以找到我们创建的pdf文档,打开来看看:

其中test.pdf是我们创建的目标文件,test1.pdf是我放到该路径的一个文件,用于测试本地URL。

 

打开test.pdf:

将鼠标放到第一页右上角的黑色矩形上,可以见到www.baidu.com的链接,点击该矩形可以打开百度的主页。

 

再看看第二页:

将鼠标放到第二页右上角黑色矩形的范围,可以看到test.pdf链接提示,点击该矩形可以打开该目录下的test.pdf(当然这个是之前代码设定好的本地文件的地址)。

 

 

但是直接用iOS程序打开,是无法响应链接的,这个还要继续修正,PDF之旅将持续更新。

 

 

后续更新:

上次留了个坑给大家,现在填一下,参考网址:Drawing and Printing Guide for iOS

上次写完该博客的时候留了一段僵尸代码:

 

  1. /* 
  2. // 为某一个矩形设置destination,这里destination的作用还不是很明白,保留 
  3. CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("Hello world"), CGRectMake(50.0, 300.0, 100.0, 100.0)); 
  4. CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5); 
  5. CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0)); 
  6.  */  

也就是关于set destination的问题,这几天偶然看到了上面的guide,今天找到了理解方法并写了代码验证,现在填一下这个坑吧。

 

 

看看SDK中的该方法:

 

 

  1. /* Create a PDF destination named `name' at `point' in the current page of 
  2.    the PDF context `context'. */  
  3.   
  4. CG_EXTERN void CGPDFContextAddDestinationAtPoint(CGContextRef context,  
  5.   CFStringRef name, CGPoint point)  
  6.   CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);  


该方法创建了一个跳转的终点,name指定了该跳转点的名字,起到标识该点以及和下面的rect配对的作用,point则指定跳转的位置,对应的参考坐标系为左下角为原点。

 

 

以及:

 

  1. /* Specify a destination named `name' to jump to when clicking in `rect' of 
  2.    the current page of the PDF context `context'. */  
  3.   
  4. CG_EXTERN void CGPDFContextSetDestinationForRect(CGContextRef context,  
  5.   CFStringRef name, CGRect rect)  
  6.   CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);  


name指定了跳转点的名字,必须和上面的destination name配对,否则点击该矩形无效,rect为接受点击的矩形范围。

 

 

示意图如下:

(1)在rect上设置的跳转点的name为:"Chapter_1"。

(2)在某一个page上设置了跳转点point,其name为"Chapter_1",和rect上设置的跳转点配对。

(3)点击rect,页面将跳转到point对应的位置。

 

 

下面代码测试一下:

在PDF第一页中设置如下:

 

 

  1. // 为一个矩形设置一个跳转终点  
  2. CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("page2"), CGRectMake(50.0, 300.0, 100.0, 100.0));  
  3. CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5);  
  4. CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0));  


在第二页中设置如下:

 

  1. // 在右下角写一段文字:"Page 2"  
  2. CGContextSelectFont(myPDFContext, "Helvetica", 30, kCGEncodingMacRoman);  
  3. CGContextSetTextDrawingMode (myPDFContext, kCGTextFill);  
  4. CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  5. const charchar *text = [@"Page 2" UTF8String];  
  6. CGContextShowTextAtPoint (myPDFContext, 120, 80, text, strlen(text));  
  7. CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page2"), CGPointMake(120.0, 120.0));  


跑起来看看(直接在Mac上打开):

 


 

点击后跳转到我们预先设置好的目的地:

 

在这里要注意的是:

destination point的设置的坐标系是左下角为(0, 0),x轴向右增长,y轴向上增长。而不是左上角为(0, 0),x轴向右增长,y轴向下增长。

 

如果出现了多个desination name相同的情况呢?经测试是跳转到最后一个设定的同名的destination point上,因为该点覆盖了以上所有点,例如在第一页中加上:

 

 

  1. // 为一个矩形设置一个跳转终点  
  2. CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page"), CGPointMake(120.0, 400.0));  
  3. CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("page"), CGRectMake(50.0, 300.0, 100.0, 100.0));  
  4. CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5);  
  5. CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0));  


跑起来的话还是跳转到第二页的point上。

 

 

说得比较乱,贴上完整代码,有兴趣的可以自己测试看看:

 

 

  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.     // 1.创建media box  
  6.     CGFloat myPageWidth = self.view.bounds.size.width;  
  7.     CGFloat myPageHeight = self.view.bounds.size.height;  
  8.     CGRect mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);  
  9.       
  10.       
  11.     // 2.设置pdf文档存储的路径  
  12.     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
  13.     NSString *documentsDirectory = paths[0];  
  14.     NSString *filePath = [documentsDirectory stringByAppendingString:@"/test.pdf"];  
  15.     // NSLog(@"%@", filePath);  
  16.     const charchar *cfilePath = [filePath UTF8String];  
  17.     CFStringRef pathRef = CFStringCreateWithCString(NULL, cfilePath, kCFStringEncodingUTF8);  
  18.       
  19.       
  20.     // 3.设置当前pdf页面的属性  
  21.     CFStringRef myKeys[3];  
  22.     CFTypeRef myValues[3];  
  23.     myKeys[0] = kCGPDFContextMediaBox;  
  24.     myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));  
  25.     myKeys[1] = kCGPDFContextTitle;  
  26.     myValues[1] = CFSTR("我的PDF");  
  27.     myKeys[2] = kCGPDFContextCreator;  
  28.     myValues[2] = CFSTR("Jymn_Chen");  
  29.     CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const voidvoid **) myKeys, (const voidvoid **) myValues, 3,  
  30.                                                         &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);  
  31.       
  32.       
  33.     // 4.获取pdf绘图上下文  
  34.     CGContextRef myPDFContext = MyPDFContextCreate (&mediaBox, pathRef);  
  35.       
  36.       
  37.     // 5.开始描绘第一页页面  
  38.     CGPDFContextBeginPage(myPDFContext, pageDictionary);  
  39.     CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);  
  40.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));  
  41.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);  
  42.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));  
  43.       
  44.     // 为一个矩形设置URL链接www.baidu.com  
  45.     CFURLRef baiduURL = CFURLCreateWithString(NULL, CFSTR("http://www.baidu.com"), NULL);  
  46.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  47.     CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));  
  48.     CGPDFContextSetURLForRect(myPDFContext, baiduURL, CGRectMake (200, 200, 100, 200 ));  
  49.       
  50.     // 为一个矩形设置一个跳转终点  
  51.     CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page"), CGPointMake(120.0, 400.0));  
  52.     CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("page"), CGRectMake(50.0, 300.0, 100.0, 100.0)); // 跳转点的name为page  
  53. //    CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("page2"), CGRectMake(50.0, 300.0, 100.0, 100.0)); // 跳转点的name为page2  
  54.     CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5);  
  55.     CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0));  
  56.       
  57.     CGPDFContextEndPage(myPDFContext);  
  58.       
  59.       
  60.     // 6.开始描绘第二页页面  
  61.     // 注意要另外创建一个page dictionary  
  62.     CFDictionaryRef page2Dictionary = CFDictionaryCreate(NULL, (const voidvoid **) myKeys, (const voidvoid **) myValues, 3,  
  63.                                                         &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);  
  64.     CGPDFContextBeginPage(myPDFContext, page2Dictionary);  
  65.       
  66.     // 在左下角画两个矩形  
  67.     CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);  
  68.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));  
  69.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);  
  70.     CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));  
  71.       
  72.     // 在右下角写一段文字:"Page 2"  
  73.     CGContextSelectFont(myPDFContext, "Helvetica", 30, kCGEncodingMacRoman);  
  74.     CGContextSetTextDrawingMode (myPDFContext, kCGTextFill);  
  75.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  76.     const charchar *text = [@"Page 2" UTF8String];  
  77.     CGContextShowTextAtPoint (myPDFContext, 120, 80, text, strlen(text));  
  78. //    CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page2"), CGPointMake(120.0, 120.0));  // 跳转点的name为page2  
  79. //    CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page"), CGPointMake(120.0, 120.0)); // 跳转点的name为page  
  80.       
  81.     // 为右上角的矩形设置一段file URL链接,打开本地文件  
  82.     NSURL *furl = [NSURL fileURLWithPath:@"/Users/one/Library/Application Support/iPhone Simulator/7.0/Applications/3E7CB341-693A-4FE4-8FE5-A827A5210F0A/Documents/test1.pdf"];  
  83.     CFURLRef fileURL = (__bridge CFURLRef)furl;  
  84.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  85.     CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));  
  86.     CGPDFContextSetURLForRect(myPDFContext, fileURL, CGRectMake (200, 200, 100, 200 ));  
  87.       
  88.     CGPDFContextEndPage(myPDFContext);  
  89.       
  90.       
  91.     // 7.创建第三页内容  
  92.     CFDictionaryRef page3Dictionary = CFDictionaryCreate(NULL, (const voidvoid **) myKeys, (const voidvoid **) myValues, 3,  
  93.                                                          &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);  
  94.     CGPDFContextBeginPage(myPDFContext, page3Dictionary);  
  95.     CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);  
  96.     CGPDFContextEndPage(myPDFContext);  
  97.       
  98.       
  99.     // 8.释放创建的对象  
  100.     CFRelease(page3Dictionary);  
  101.     CFRelease(page2Dictionary);  
  102.     CFRelease(pageDictionary);  
  103.     CFRelease(myValues[0]);  
  104.     CGContextRelease(myPDFContext);  
  105. }  
  106.  

转载于:https://my.oschina.net/HeroOneHY/blog/903894

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值