关于CoreText,自己做一些小纪录

    首先我是萌新,ios开发新手,一些大牛觉得不对,错误的,请勿喷我怎么吃螃蟹,请告诉我怎么吃就好了,谢谢了。

    之前因为项目需求,要给小说阅读器中内容加上下划线,但是这个下划线又跟其他的不一样,这个下划线要在基线的位置再靠上一些,当初不明白为什么要这么做,而且效果也很难看,用户体验也不好,但是老大说这能防止pdf扫描vip章节内容,所以还是得老老实实的做,就对coreText开始了研究。

本次主要用到的点在与CTLineRef.接下来先对其做一些了解。

1.字符(Character)和字形(Glyphs)

排版系统中文本显示的一个重要的过程就是字符到字形的转换,字符是信息本身的元素,而字形是字符的图形表征,字符还会有其它表征比如发音。 字符在计算机中其实就是一个编码,某个字符集中的编码,比如Unicode字符集,就囊括了大都数存在的字符。 而字形则是图形,一般都存储在字体文件中,字形也有它的编码,也就是它在字体中的索引。 一个字符可以对应多个字形(不同的字体,或者同种字体的不同样式:粗体斜体等);多个字符也可能对应一个字形,比如字符的连写( Ligatures)。

关于CoreText,自己做一些小纪录_第1张图片

下面就来看看字形的各个参数也就是所谓的字形度量Glyph Metrics,其实我认为就是一个小时侯学写字母的时候的作业本一样的各个线


关于CoreText,自己做一些小纪录_第2张图片

bounding box(边界框 bbox),这是一个假想的框子,它尽可能紧密的装入字形。

baseline(基线),一条假想的线,一行上的字形都以此线作为上下位置的参考,在这条线的左侧存在一个点叫做基线的原点,

ascent(上行高度)从原点到字体中最高(这里的高深都是以基线为参照线的)的字形的顶部的距离,ascent是一个正值

descent(下行高度)从原点到字体中最深的字形底部的距离,descent是一个负值(比如一个字体原点到最深的字形的底部的距离为2,那么descent就为-2)

linegap(行距),linegap也可以称作leading(其实准确点讲应该叫做External leading),行高lineHeight则可以通过 ascent + |descent| + linegap 来计算。

一些Metrics专业知识还可以参考Free Type的文档 Glyph metrics,其实iOS就是使用Free Type库来进行字体渲染的。

以上图片和部分概念来自苹果文档 Querying Font Metrics ,Text Layout


2.坐标系

苹果编程中的坐标系不明白为什么会各有不同。 传统的Mac中的坐标系的原点在左下角,比如NSView默认的坐标系,原点就在左下角。但Mac中有些View为了其实现的便捷将原点变换到左上角,像NSTableView的坐标系坐标原点就在左上角。iOS UIKit的UIView的坐标系原点在左上角。


看完上边儿这些,该上代码勒。


先拿一个定义好的属性字符串。

NSMutableAttributedString *attrString = [[NSMutableAttributedString  alloc] initWithString:self.text];//这个self.text 就是你要用到的字符串

[attrString setAttributes:self.coreTextAttributes range:NSMakeRange(0, attrString.length)];//这里的self.coreTextAttributes就是一个字典,来配置这个属性字符串的,可在他的set方法中随意设置,比如颜色,下划线,删除线,字型等等


然后把属性字符串放到frame中

CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrString);

CGPathRef path = CGPathCreateWithRect(self.bounds, NULL);

if (_ctFrame != NULL) {  //这里的_ctFrame 就是一个装有字符属性的集合

CFRelease(_ctFrame), _ctFrame = NULL;

}

_ctFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);

CFRelease(path);

CFRelease(frameSetter);


做完了上面这些,就该开始画字了。

在- (void)drawRect:(CGRect)rect方法中

if (!_ctFrame) return;

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetTextMatrix(context, CGAffineTransformIdentity);

CTFrameDraw(_ctFrame, context);

这样写的效果如图


关于CoreText,自己做一些小纪录_第3张图片

是镜像过来的,要再翻过来

CGAffineTransform transform = CGAffineTransformMake(1,0,0,-1,0,self.bounds.size.height);

CGContextConcatCTM(context, transform);

加上以上这两句就可以了,就顺利的将字画到了画布上。


再之后给文字加个方框

CGMutablePathRef path = CGPathCreateMutable();


CGPathAddRect(path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));

CFArrayRef lines = CTFrameGetLines(_ctFrame);

CFIndex linecount = CFArrayGetCount(lines);

CGPoint origins[linecount];

CTFrameGetLineOrigins(_ctFrame, CFRangeMake(0, 0), origins);

NSInteger lineIndex = 0;

for (id oneLine in (__bridge NSArray *)lines) {

CGRect lineBounds = CTLineGetImageBounds((CTLineRef)oneLine, context);

lineBounds.origin.x += origins[lineIndex].x;

lineBounds.origin.y += origins[lineIndex].y;

lineIndex ++;

CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);

CGContextSetLineWidth(context, 1.0);

CGPoint poins[] = {CGPointMake(lineBounds.origin.x, lineBounds.origin.y),CGPointMake(lineBounds.origin.x+lineBounds.size.width, lineBounds.origin.y),CGPointMake(lineBounds.origin.x+lineBounds.size.width, lineBounds.origin.y+lineBounds.size.height),CGPointMake(lineBounds.origin.x, lineBounds.origin.y+lineBounds.size.height)};//绘制四边,位置随意调整,位置可以调整之后也就可以实现我的下划线在任何位置了

CGContextAddLines(context, poins, 4);

CGContextClosePath(context);

CGContextStrokePath(context);

}

}


其中主要是取CTFrameRef集合中的CTLines ,在其中他包含了字符的各种属性,rang等

第一次写文章,主要是当时搞这个东西走了弯路,对coreText的不了解,也在csdn和cocoachina上问了好多人,都没解决,防止以后忘记,自己再纪录一下。


我目前做的项目是做的小说阅读器,网上的素材真的是不多,现在也算是写的差不多了,但是在预加载,内存缓存和磁盘缓存上做的还是不行,希望有做阅读器这方面有好的方案的,希望可以教教小弟,第一篇小文 也就搞定勒。

你可能感兴趣的:(关于CoreText,自己做一些小纪录)