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

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

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

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

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

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

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


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);

这样写的效果如图


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

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上问了好多人,都没解决,防止以后忘记,自己再纪录一下。


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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • 1.iOS中的round、ceil、floor函数略解 round如果参数是小数,则求本身的四舍五入.ceil如果...
    K_Gopher阅读 1,182评论 1 0
  • 本文所涉及的代码你可以在这里下载到https://github.com/kejinlu/CTTest,包含两个项目...
    eb99d15a673d阅读 1,249评论 0 6
  • 目前社交类型的App也是层出不穷,无论是QQ的说说,还是微信的动态,微博的帖子。这种类型的App都会涉及到点赞文本...
    墨香茉香阅读 658评论 0 3
  • CoreText是iOS/OSX中文本显示的一个底层框架,它是用C语言写成的,有快速简单的优势。iOS中的Text...
    小猫仔阅读 4,947评论 2 9
  • 最近在网上看了一些大牛的文章,自己也试着写了一下,感觉图文混排真的很强大。 废话不多说,开始整 先上效果图跟代码,...
    AllureJM阅读 978评论 0 1