zoukankan      html  css  js  c++  java
  • 总结用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:@"
    " withString:@"
    "];
        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测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。
     测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。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; }

    绘制出来的效果如下:

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

  • 相关阅读:
    @RequestParam注解使用:Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.
    cglib动态代理导致注解丢失问题及如何修改注解允许被继承
    springboot Autowired BeanNotOfRequiredTypeException
    git根据用户过滤提交记录
    不同包下,相同数据结构的两个类进行转换
    How to use Jackson to deserialise an array of objects
    jooq实践
    java如何寻找main函数对应的类
    Python--matplotlib
    Python 和 Scikit-Learn
  • 原文地址:https://www.cnblogs.com/endtel/p/4863064.html
Copyright © 2011-2022 走看看