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

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


直接上代码吧:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 1.创建media box
    CGFloat myPageWidth = self.view.bounds.size.width;
    CGFloat myPageHeight = self.view.bounds.size.height;
    CGRect mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);
    
    
    // 2.设置pdf文档存储的路径
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = paths[0];
    NSString *filePath = [documentsDirectory stringByAppendingString:@"/test.pdf"];
    // NSLog(@"%@", filePath);
    const char *cfilePath = [filePath UTF8String];
    CFStringRef pathRef = CFStringCreateWithCString(NULL, cfilePath, kCFStringEncodingUTF8);
    
    
    // 3.设置当前pdf页面的属性
    CFStringRef myKeys[3];
    CFTypeRef myValues[3];
    myKeys[0] = kCGPDFContextMediaBox;
    myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
    myKeys[1] = kCGPDFContextTitle;
    myValues[1] = CFSTR("我的PDF");
    myKeys[2] = kCGPDFContextCreator;
    myValues[2] = CFSTR("Creator Name");
    CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 3,
                                                        &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
    
    
    // 4.获取pdf绘图上下文
    CGContextRef myPDFContext = MyPDFContextCreate (&mediaBox, pathRef);
    
    
    // 5.开始描绘第一页页面
    CGPDFContextBeginPage(myPDFContext, pageDictionary);
    CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
    
    // 为一个矩形设置URL链接www.baidu.com
    CFURLRef baiduURL = CFURLCreateWithString(NULL, CFSTR("http://www.baidu.com"), NULL);
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));
    CGPDFContextSetURLForRect(myPDFContext, baiduURL, CGRectMake (200, 200, 100, 200 ));
    
    CGPDFContextEndPage(myPDFContext);
    
    
    
    // 6.开始描绘第二页页面
    // 注意要另外创建一个page dictionary
    CFDictionaryRef page2Dictionary = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 3,
                                                        &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
    CGPDFContextBeginPage(myPDFContext, page2Dictionary);
    
    // 在左下角画两个矩形
    CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
    
    // 在右下角写一段文字:"Hello world"
    CGContextSelectFont(myPDFContext, "Helvetica", 30, kCGEncodingMacRoman);
    CGContextSetTextDrawingMode (myPDFContext, kCGTextFill);
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    const char *text = [@"Hello world" UTF8String];
    CGContextShowTextAtPoint (myPDFContext, 120, 120, text, strlen(text));
    
    /*
    // 为某一个矩形设置destination,这里destination的作用还不是很明白,保留
    CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("Hello world"), CGRectMake(50.0, 300.0, 100.0, 100.0));
    CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5);
    CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0));
     */
    
    // 为右上角的矩形设置一段file URL链接,打开本地文件
    NSURL *furl = [NSURL fileURLWithPath:@"/Users/one/Library/Application Support/iPhone Simulator/7.0/Applications/3E7CB341-693A-4FE4-8FE5-A827A5210F0A/Documents/test1.pdf"];
    CFURLRef fileURL = (__bridge CFURLRef)furl;
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));
    CGPDFContextSetURLForRect(myPDFContext, fileURL, CGRectMake (200, 200, 100, 200 ));
    
    CGPDFContextEndPage(myPDFContext);
    
    
    
    // 7.释放创建的对象
    CFRelease(page2Dictionary);
    CFRelease(pageDictionary);
    CFRelease(myValues[0]);
    CGContextRelease(myPDFContext);
}

/*
 * 获取pdf绘图上下文
 * inMediaBox指定pdf页面大小
 * path指定pdf文件保存的路径
 */
CGContextRef MyPDFContextCreate (const CGRect *inMediaBox, CFStringRef path)
{
    CGContextRef myOutContext = NULL;
    CFURLRef url;
    CGDataConsumerRef dataConsumer;
    
    url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, false);
    
    if (url != NULL)
    {
        dataConsumer = CGDataConsumerCreateWithURL(url);
        if (dataConsumer != NULL)
        {
            myOutContext = CGPDFContextCreate (dataConsumer, inMediaBox, NULL);
            CGDataConsumerRelease (dataConsumer);
        }
        CFRelease(url);
    }
    return myOutContext;
}

代码注释基本说明了所有问题,这里要注意的就是如果 要创建多页的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

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

    /*
    // 为某一个矩形设置destination,这里destination的作用还不是很明白,保留
    CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("Hello world"), CGRectMake(50.0, 300.0, 100.0, 100.0));
    CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5);
    CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0));
     */
也就是关于set destination的问题,这几天偶然看到了上面的guide,今天找到了理解方法并写了代码验证,现在填一下这个坑吧。


看看SDK中的该方法:

/* Create a PDF destination named `name' at `point' in the current page of
   the PDF context `context'. */

CG_EXTERN void CGPDFContextAddDestinationAtPoint(CGContextRef context,
  CFStringRef name, CGPoint point)
  CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);

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


以及:

/* Specify a destination named `name' to jump to when clicking in `rect' of
   the current page of the PDF context `context'. */

CG_EXTERN void CGPDFContextSetDestinationForRect(CGContextRef context,
  CFStringRef name, CGRect rect)
  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第一页中设置如下:

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

在第二页中设置如下:

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

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



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



在这里要注意的是:

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


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

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

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


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

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 1.创建media box
    CGFloat myPageWidth = self.view.bounds.size.width;
    CGFloat myPageHeight = self.view.bounds.size.height;
    CGRect mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);
    
    
    // 2.设置pdf文档存储的路径
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = paths[0];
    NSString *filePath = [documentsDirectory stringByAppendingString:@"/test.pdf"];
    // NSLog(@"%@", filePath);
    const char *cfilePath = [filePath UTF8String];
    CFStringRef pathRef = CFStringCreateWithCString(NULL, cfilePath, kCFStringEncodingUTF8);
    
    
    // 3.设置当前pdf页面的属性
    CFStringRef myKeys[3];
    CFTypeRef myValues[3];
    myKeys[0] = kCGPDFContextMediaBox;
    myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
    myKeys[1] = kCGPDFContextTitle;
    myValues[1] = CFSTR("我的PDF");
    myKeys[2] = kCGPDFContextCreator;
    myValues[2] = CFSTR("Jymn_Chen");
    CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 3,
                                                        &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
    
    
    // 4.获取pdf绘图上下文
    CGContextRef myPDFContext = MyPDFContextCreate (&mediaBox, pathRef);
    
    
    // 5.开始描绘第一页页面
    CGPDFContextBeginPage(myPDFContext, pageDictionary);
    CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
    
    // 为一个矩形设置URL链接www.baidu.com
    CFURLRef baiduURL = CFURLCreateWithString(NULL, CFSTR("http://www.baidu.com"), NULL);
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));
    CGPDFContextSetURLForRect(myPDFContext, baiduURL, CGRectMake (200, 200, 100, 200 ));
    
    // 为一个矩形设置一个跳转终点
    CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page"), CGPointMake(120.0, 400.0));
    CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("page"), CGRectMake(50.0, 300.0, 100.0, 100.0)); // 跳转点的name为page
//    CGPDFContextSetDestinationForRect(myPDFContext, CFSTR("page2"), CGRectMake(50.0, 300.0, 100.0, 100.0)); // 跳转点的name为page2
    CGContextSetRGBFillColor(myPDFContext, 1, 0, 1, 0.5);
    CGContextFillEllipseInRect(myPDFContext, CGRectMake(50.0, 300.0, 100.0, 100.0));
    
    CGPDFContextEndPage(myPDFContext);
    
    
    // 6.开始描绘第二页页面
    // 注意要另外创建一个page dictionary
    CFDictionaryRef page2Dictionary = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 3,
                                                        &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
    CGPDFContextBeginPage(myPDFContext, page2Dictionary);
    
    // 在左下角画两个矩形
    CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
    
    // 在右下角写一段文字:"Page 2"
    CGContextSelectFont(myPDFContext, "Helvetica", 30, kCGEncodingMacRoman);
    CGContextSetTextDrawingMode (myPDFContext, kCGTextFill);
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    const char *text = [@"Page 2" UTF8String];
    CGContextShowTextAtPoint (myPDFContext, 120, 80, text, strlen(text));
//    CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page2"), CGPointMake(120.0, 120.0));  // 跳转点的name为page2
//    CGPDFContextAddDestinationAtPoint(myPDFContext, CFSTR("page"), CGPointMake(120.0, 120.0)); // 跳转点的name为page
    
    // 为右上角的矩形设置一段file URL链接,打开本地文件
    NSURL *furl = [NSURL fileURLWithPath:@"/Users/one/Library/Application Support/iPhone Simulator/7.0/Applications/3E7CB341-693A-4FE4-8FE5-A827A5210F0A/Documents/test1.pdf"];
    CFURLRef fileURL = (__bridge CFURLRef)furl;
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (200, 200, 100, 200 ));
    CGPDFContextSetURLForRect(myPDFContext, fileURL, CGRectMake (200, 200, 100, 200 ));
    
    CGPDFContextEndPage(myPDFContext);
    
    
    // 7.创建第三页内容
    CFDictionaryRef page3Dictionary = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 3,
                                                         &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
    CGPDFContextBeginPage(myPDFContext, page3Dictionary);
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 0, 1);
    CGPDFContextEndPage(myPDFContext);
    
    
    // 8.释放创建的对象
    CFRelease(page3Dictionary);
    CFRelease(page2Dictionary);
    CFRelease(pageDictionary);
    CFRelease(myValues[0]);
    CGContextRelease(myPDFContext);
}



  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值