总结用CoreText绘制文本时遇到的问题以及解决办法

关于CoreText不做解释。用的人自然知道这个是干什么的。

功能非常强大,可以绘制文本,图片等。

这次用的Xcode7.0的版本。所以之前很多方法,现在不能用。也不是不能用,就是有黄色警告很不爽。

这次要实现在的功能是把一套试题绘制在PDF上面。仅是选择题。

CoreText是MacOS的框架。坐标是以左下为原点。与我们要用的APP应用的坐标有所不同,绘制的时候,关于坐标的问题,也是让我非常头痛与纠结的一个问题。

看了网络上面各种办法,一般是转换坐标系。把原点转换到左上。

代码如下:

//    CGContextSetTextMatrix(pdfContext, CGAffineTransformIdentity); //它可以用来为每一个显示的字形单独设置变形矩阵。设置字形变换矩阵为CGAffineTransformIdentity,也就是说每一个字形都不做图形变换
    //转换Y轴坐标,  底层坐标与cocoa 组件不同 Y轴相反
//    CGContextTranslateCTM(pdfContext, 0, CGRectGetHeight(self.view.bounds));
//    CGContextScaleCTM(pdfContext, 1, -1);

之所以打了//,是因为我并没有用这个。当初也是试过的。后来发现没用,不知道是我的方法没对还是怎么的。要是有人能指导一下就好了。

下面说一下我绘制的一些步骤以及要注意的地方,方便自己以后查阅,同时为新入门的同学做个参考。

绘制的主要代码如下:

有些地方是可以优化的,但是先就这样了。那个pageRect是整个页面的大小,在绘制的时候,没有用到这个参数。

值得注意的是,CoreText绘制的是带属性的字符串,关于其释义,可以百度。

- (void)drawTextWithPageRect:(CGRect)pageRect andContext:(CGContextRef)context andText:(NSString *)text byDrawInRect:(CGRect)inRect{
    CGMutablePathRef pathRef = CGPathCreateMutable(); //1  这里你需要创建一个用于绘制文本的路径区域
    CGPathAddRect(pathRef, NULL, inRect);
    //去掉空行
    
//    NSString *labelString = text;
//    NSString *myString = [labelString stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"];
    NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:text]; //  2 在 Core Text 中使用 NSAttributedString 而不是 NSString
    //为所有文本设置字体
    [attrString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:18] range:NSMakeRange(0, [attrString length])];
    //设置字体颜色
    //[attrString addAttribute:(NSString *)(kCTForegroundColorAttributeName) value:(id)[UIColor greenColor] range:NSMakeRange(0, [attrString length])]; //这个属性的文本的前景颜色。与该属性的值必须是一个CGColor对象。默认值是黑色的。
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString); //3  管理您的字体引用和文本绘制帧。
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                                                CFRangeMake(0, [attrString length]), pathRef, NULL);
    
    CTFrameDraw(frame, context); //4  CTFrameDraw 将 frame 描述到设备上下文。
    
    CFRelease(frame); //5  最后,释放所有使用的对象。
    CFRelease(pathRef);
    CFRelease(framesetter);
}

 

绘制的时候是一条一条的绘制,比如一个选择题有一个问题和四个答案,那就要分5次绘制。

下面贴上代码

- (void)createPDF{
    
    //获取路径
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *saveDirectory = [paths firstObject];
    NSString *saveFileName = @"test.pdf";
    NSString *newFilePath = [saveDirectory stringByAppendingPathComponent:saveFileName];
    _pdfPath = newFilePath;
//    if([[NSFileManager defaultManager] fileExistsAtPath:newFilePath]){
//        [[NSFileManager defaultManager] removeItemAtPath:newFilePath error:nil];
//    }
    NSLog(@"newFilePath:%@",newFilePath);
    const char *filename = [newFilePath UTF8String];
    
    //设置页面大小
    CGRect pageRect = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64);
    //1024 * 768
   // NSLog(@"pageRect w:%f  h:%f",pageRect.size.width, pageRect.size.height);
    
    //关联上下文对象
    CGContextRef pdfContext;
    CFStringRef path;
    CFURLRef url;
    
    path = CFStringCreateWithCString(NULL, filename, kCFStringEncodingUTF8);
    url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, 0);
    CFRelease(path);
    pdfContext = CGPDFContextCreateWithURL(url, &pageRect, nil);
    CFRelease(url);
//    CGContextSetTextMatrix(pdfContext, CGAffineTransformIdentity); //它可以用来为每一个显示的字形单独设置变形矩阵。设置字形变换矩阵为CGAffineTransformIdentity,也就是说每一个字形都不做图形变换
    //转换Y轴坐标,  底层坐标与cocoa 组件不同 Y轴相反
//    CGContextTranslateCTM(pdfContext, 0, CGRectGetHeight(self.view.bounds));
//    CGContextScaleCTM(pdfContext, 1, -1);
    
    //NSString *text = @"11测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。\n 测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。11222 english text";
    
//    //以前的方法  drawAtPoint: 现在用Core Text替换了 可以试试drawInRect
//    [@"hello world" drawAtPoint:CGPointMake(80, 80) withFont:[UIFont systemFontOfSize:18]];  //绘制汉字
    
    //设置一个Y轴上的偏移
    CGFloat y = 50;
    BOOL flag = YES;  //标记是否开始新的一页
    //需要画每个问题和答案选项
    for (int i = 0; i < _createArray.count; i++) {
        if (flag) {
            //开始绘制pdf 开始新一页
            CGContextBeginPage(pdfContext, &pageRect);
            //  画一个黑色的边框  页边距50
            CGContextStrokeRect(pdfContext, CGRectMake(40, 40, pageRect.size.width - 80, pageRect.size.height - 80));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:@"成都****信息技术有限公司" byDrawInRect:CGRectMake(50, pageRect.size.height - 10, 240, -20)];
            //设置一个Y轴上的偏移
            y = 0;
            flag = NO;
        }
        Question *q = _createArray[i];
        //题目
        
        CGFloat strHeight = [self calulateHeightForString:q.question FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
        CGRect inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
        [self drawTextWithPageRect:pageRect andContext:pdfContext andText:[NSString stringWithFormat:@"%d.%@", i + 1, q.question] byDrawInRect:inRect];
        y += strHeight ;
        
        //选项 //pageRect.size.height - 50 - y
        strHeight = [self calulateHeightForString:q.item1 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
        inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
        [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item1  byDrawInRect:inRect];
        y += strHeight;
        
        strHeight = [self calulateHeightForString:q.item2 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
        inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
        [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item2  byDrawInRect:inRect];
        y += strHeight;
        
        strHeight = [self calulateHeightForString:q.item3 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
        inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
        [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item3  byDrawInRect:inRect];
        y += strHeight;
        
        
        strHeight = [self calulateHeightForString:q.item4 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
        inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
        [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item4  byDrawInRect:inRect];
        y += strHeight;
        if(!hideAnswer){  //判断是否显示答案
            strHeight = [self calulateHeightForString:q.answer FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
            inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:[NSString stringWithFormat:@"答案:%@", q.answer] byDrawInRect:inRect];
            y += strHeight;
        }
        y = y + 5; //每个问题结束下面额外给5个点
        
        if ((pageRect.size.height - 50 - y - 40) < [self getHeightForQuestionCell:(i + 1)]) { //后面的40减去下边框的距离
            //结束当前页 开始新一页
            CGContextEndPage (pdfContext);
            flag = YES;
        }

    }
    
    //循环结束后,也要结束绘制最后一页
    CGContextEndPage (pdfContext);
    
    CGContextRelease (pdfContext);
    
    
}

上面获取路径部分,主要是获取PDF的上下文。绘制的时候,要注意,因为坐标系是左下为原点,往PDF绘文字时,文字虽然是正的,但是是往上面绘制的

LINDESPACING 是行间距

后面的if是判断剩下的距离能不能绘制下一题。

解决办法:把要绘制的矩形区域的高度前面加负号“-”

每一次绘制,都要开始新的一页

CGContextBeginPage(pdfContext, &pageRect);

与其对应的,在一页结束后,一定要结束一页 

CGContextEndPage (pdfContext);

之前就忘了写最后一个结束,导致出错,还找了好一会才找到BUG原因。

里面还用到一个计算文字高度的方法,一起贴上吧,也方便以后万一忘了好查询。

- (CGFloat)calulateHeightForString:(NSString *)str  FontType:(UIFont *)fontType RowWidth:(CGFloat) rowWidth{
    if (str==nil){
        return 0;
    }
    CGSize maxSize=CGSizeMake(rowWidth, 99999);
    //以前的方法
//    CGSize  strSize=[str sizeWithFont:[UIFont systemFontOfSize:15]constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
 CGRect strSize = [str boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: fontType} context:nil]; 
// [lrcStr sizeWithFont:[UIFont systemFontOfSize:self.fontSize] forWidth:rowWidth lineBreakMode:UILineBreakModeWordWrap]; //这个方法被抛弃了
//return strSize.height;
return strSize.size.height; }

绘制出来的效果如下:

以上内容由个人总结,不完善的地方,欢迎指正,共同学习。

posted @ 2015-10-09 10:08  MokeyChan  阅读(850)  评论(0编辑  收藏  举报