初识CoreText

一、基本知识介绍

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

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

Roman 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库来进行字体渲染的。

2.坐标系

首先不得不说 苹果编程中的坐标系花样百出,经常让开发者措手不及。 传统的Mac中的坐标系的原点在左下角,比如NSView默认的坐标系,原点就在左下角。但Mac中有些View为了其实现的便捷将原点变换到左上角,像NSTableView的坐标系坐标原点就在左上角。iOS UIKit的UIView的坐标系原点在左上角。 
往底层看,Core Graphics的context使用的坐标系的原点是在左下角。而在iOS中的底层界面绘制就是通过Core Graphics进行的,那么坐标系列是如何变换的呢? 在UIView的drawRect方法中我们可以通过UIGraphicsGetCurrentContext()来获得当前的Graphics Context。drawRect方法在被调用前,这个Graphics Context被创建和配置好,你只管使用便是。如果你细心,通过CGContextGetCTM(CGContextRef c)可以看到其返回的值并不是CGAffineTransformIdentity,通过打印出来看到值为

Printing description of contextCTM:
(CGAffineTransform) contextCTM = {
        a = 1
        b = 0
        c = 0
        d = -1
        tx = 0
        ty = 460
}

这是非retina分辨率下的结果,如果是如果是retina上面的a,d,ty的值将会乘2,如果是iPhone 5,ty的值会再大些。 但是作用都是一样的就是将上下文空间坐标系进行了flip,使得原本左下角原点变到左上角,y轴正方向也变换成向下。

上面说了一大堆,下面进入正题,Core Text一开始便是定位于桌面的排版系统,使用了传统的原点在左下角的坐标系,所以它在绘制文本的时候都是参照左下角的原点进行绘制的。 但是iOS的UIView的drawRect方法的context被做了次flip,如果你啥也不做处理,直接在这个context上进行Core Text绘制,你会发现文字是镜像且上下颠倒。 如图所示

所以首先我们需要把坐标系翻转过来,插入一下代码即可

CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

接下来我们实现一个最基本最简单的绘制,代码如下

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    // 获取当前上下文
    CGContextRef context = UIGraphicsGetCurrentContext();

    // 翻转坐标系
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // 绘制路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, self.bounds);

    // 创建绘制的字符串和CTFrame
    NSAttributedString *attString = [[NSAttributedString alloc] initWithString:@"Hello core text world!"];
    CTFramesetterRef framesetter =
    CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
    CTFrameRef frame =
    CTFramesetterCreateFrame(framesetter,
                             CFRangeMake(0, [attString length]), path, NULL);

    // 绘制
    CTFrameDraw(frame, context);

    // 释放内存
    CFRelease(frame);
    CFRelease(path);
    CFRelease(framesetter);
}

实现效果如图所示:

 这里来解释一下CTFramesetter和CTFrame,首先我们来看两张图,可以帮助我们更好的理解CoreText的对象模型

      

CTFrame可以理解为一个整体的画布由很多行(CTLine)组成,而每一行又由一个或者多个小方块(CTRun)组成,我们不需要自己创建CTRun,Core Text将根据NSAttributedString的属性来自动创建CTRun。每个CTRun对象对应不同的属性,正因此,你可以自由的控制字体、颜色、字间距等等信息。

CTFramesetter其实就是CTFrame的工厂方法,通过给定的NSAttributedString,生成CTRrame,同时系统自动的创建了CTTypesetter,CTTypesetter就是管理你的字体的类。

那么又如何来使用NSAttributedString呢?如何来设置行高,字体样式,大小呢?

下面列举了可以设置的属性

const CFStringRef kCTCharacterShapeAttributeName;
//字体形状属性  必须是CFNumberRef对象默认为0,非0则对应相应的字符形状定义,如1表示传统字符形状
const CFStringRef kCTFontAttributeName;
//字体属性   必须是CTFont对象
const CFStringRef kCTKernAttributeName;
//字符间隔属性 必须是CFNumberRef对象
const CFStringRef kCTLigatureAttributeName;
//设置是否使用连字属性,设置为0,表示不使用连字属性。标准的英文连字有FI,FL.默认值为1,既是使用标准连字。也就是当搜索到f时候,会把fl当成一个文字。必须是CFNumberRef 默认为1,可取0,1,2
const CFStringRef kCTForegroundColorAttributeName;
//字体颜色属性  必须是CGColor对象,默认为black
const CFStringRef kCTForegroundColorFromContextAttributeName;
 //上下文的字体颜色属性 必须为CFBooleanRef 默认为False,
const CFStringRef kCTParagraphStyleAttributeName;
//段落样式属性 必须是CTParagraphStyle对象 默认为NIL
const CFStringRef kCTStrokeWidthAttributeName;
//笔画线条宽度 必须是CFNumberRef对象,默为0.0f,标准为3.0f
const CFStringRef kCTStrokeColorAttributeName;
//笔画的颜色属性 必须是CGColorRef 对象,默认为前景色
const CFStringRef kCTSuperscriptAttributeName;
//设置字体的上下标属性 必须是CFNumberRef对象 默认为0,可为-1为下标,1为上标,需要字体支持才行。如排列组合的样式Cn1
const CFStringRef kCTUnderlineColorAttributeName;
//字体下划线颜色属性 必须是CGColorRef对象,默认为前景色
const CFStringRef kCTUnderlineStyleAttributeName;
//字体下划线样式属性 必须是CFNumberRef对象,默为kCTUnderlineStyleNone 可以通过CTUnderlineStypleModifiers 进行修改下划线风格
const CFStringRef kCTVerticalFormsAttributeName;
//文字的字形方向属性 必须是CFBooleanRef 默认为false,false表示水平方向,true表示竖直方向
const CFStringRef kCTGlyphInfoAttributeName;
//字体信息属性 必须是CTGlyphInfo对象
const CFStringRef kCTRunDelegateAttributeName
//CTRun 委托属性 必须是CTRunDelegate对象

举例来说明下吧

- (void)drawRect:(CGRect)rect {

    [super drawRect:rect];

    // 1.
    CGContextRef context = UIGraphicsGetCurrentContext();

    // 2.抓换坐标
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    NSMutableAttributedString *attriString = [[NSMutableAttributedString alloc] initWithString:@"this is test!"]
                                              ;
    //把this的字体颜色变为红色
    [attriString addAttribute:(NSString *)kCTForegroundColorAttributeName
                        value:(id)[UIColor redColor].CGColor
                        range:NSMakeRange(0, 4)];
    //把is变为绿色
    [attriString addAttribute:(NSString *)kCTForegroundColorAttributeName
                        value:(id)[UIColor greenColor].CGColor
                        range:NSMakeRange(5, 2)];
    //改变this的字体,value必须是一个CTFontRef
    [attriString addAttribute:(NSString *)kCTUnderlineStyleAttributeName
                        value:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble]
                        range:NSMakeRange(0, 4)];
    //给this加上下划线,value可以在指定的枚举中选择
    [attriString addAttribute:(NSString *)kCTUnderlineStyleAttributeName
                        value:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble]
                        range:NSMakeRange(0, 4)];
    // 绘制路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, self.bounds);
    // 创建绘制的字符串和CTFrame
    CTFramesetterRef framesetter =
    CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attriString);
    CTFrameRef frame =
    CTFramesetterCreateFrame(framesetter,
                             CFRangeMake(0, [attriString length]), path, NULL);

    // 绘制
    CTFrameDraw(frame, context);

    // 释放内存
    CFRelease(frame);
    CFRelease(path);
    CFRelease(framesetter);

}

效果如下

到这里简单的自定义绘制就完成了~

时间: 2024-11-26 01:33:50

初识CoreText的相关文章

初识Python,望君多多关照

在学习Python之前,我们接触过数据结构和网页制作.前者让我们学习如何把C语言运用的更加整齐规范,而后者让我们亲身学习如何运用所学,制作一个静态网页.通过这些课程的学习,让我对C语言产生了比较大的压力,以至于对编程.对这学期的Python课程都有一种如临大敌的感觉. 但是真的学习了这门课程,体会了编码过程中的一些固定运用方法和套路之后,也许过程中对这门课程隐隐约约产生了一点点朦胧的感觉,仿佛他也并没有想象中的那么困难,起码现在的学习让我认为,他可能没有C语言那么繁琐和麻烦.当然,以一个初学者的

初识数组排序!!!!

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>初识数组排序</title> <!--调试成功--> <style type="text/css"> *{ padding:0; margin: 0; } li,ul{ list-style: none; } #p

初识操作系统和linux

初识操作系统和linux 1.计算机系统由硬件系统和软件系统两大部分组成:是一种能接收和存储信息,并按照存储在其内部的程序对海量数据进行自动.高速地处理,然后把处理结果输出的现代化智能电子设备. 2.世界上第一台计算机是1946年诞生在美国宾州大学. 3.冯·诺依曼体系结构:1946年数学家冯·诺依曼于提出计算机硬件系统由运算器.控制器.存储器.输入设备.输出设备.摩根定律:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍.现在计算机技术进本很难遵

JAVA 初识类加载机制 第13节

JAVA 初识类加载机制 第13节 从这章开始,我们就进入虚拟机类加载机制的学习了.那么什么是类加载呢?当我们写完一个Java类的时候,并不是直接就可以运行的,它还要编译成.class文件,再由虚拟机解释给当前的操作系统去执行.这些过程都是我们看不见的,我们能看见的也就是一个.class文件.既然虚拟机要解释这些.class文件给当前的操作系统听,那么他怎么获得这些.class文件呢?虚拟机获得这些.class文件的过程就是类加载了. 所以,总结来说就是:虚拟机将.class文件从磁盘或者其他地

初识React

原文地址:北云软件-初识React 专注于UI 在MVC分层设计模式中,react常被拿来实现视图层(V).React不依赖于技术栈的其他部分,因此可以方便的在现有项目中尝试用它来实现一个小特性. 虚拟DOM React从DOM中抽象出来,给出一种更简洁的编程模型,且性能表现更好.能够通过NodeJS实现服务端渲染,通过React Native开发原生app. 数据流React实现单向.响应式数据流,减少boilerplate且比传统数据绑定更容易理解. 简洁的组件React的组件都实现了一个r

泛型的几种类型以及初识webform

今天学习的可以分为两类吧,但是学习的都是比较抽象的,不太容易掌握吧.首先我们大部分时间学习了泛型,泛型的委托,泛型接口以及枚举器,迭代器,扩展方法:最后简单的认识了webform,实现了一个简单的功能. 一.泛型 定义:泛型(generic)可以软糖多个类型共享一组代码,泛型允许我们声明类型参数化.可以用不同的类型进行实例化,说白了,就是可以用类型占位符,创建具体类型致命的真实概念.C#中提供了五种泛型,类,结构,接口,委托和方法.下面举例说明可能更容易理解, class MyStack<T>

最新计算机技术与管理科学应用专家——初识ERB

ERB管理系统:英文全称Enterprise Resource and Behavior,英文简称:ERB,中文名全称:企业资源与行为管理系统.ERB是由理文企业管理顾问有限公司首席管理师,现任商翼ERB企业管理系统项目总监吴志华先生,于2010年9月首先提出的.ERB不再单以供应链管理作为系统应用的基础,而是以企业行为与企业资源规划的最佳结合作为系统应用设计的核心基础,强调企业行为的规划.执行.监督与追溯,强调企业管理水平与员工素养的持续提升:提供企业行为与企业资源管理最佳结合的整体应用解决方

[OpenGL]环境搭建以及OpenGL初识

想往游戏行业发展的话,经常被提及到的就是OpenGL和DirectX,这两者听起来感觉是一门挺高深的技术,今天我也开始摸索学习OpenGL,那么OpenGL到底是什么?它和DirectX有什么区别和联系? OpenGL初识 OpenGL只是一套图形函数库 DirectX包含图形.声音.输入.网络等模块. 但就图形而论,DirectX的图形库性能不如OpenGL,OpenGL稳定,可以跨平台使用,DirectX只支持Windows平台,所以OpenGL还是有它的优势!OpenGL ES是OpenG

初识git

初识git 1 安装git 最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑.不过,慢慢地有人把它移植到了Windows上.现在,Git可以在Linux.Unix.Mac和Windows这几大平台上正常运行了. 要使用Git,第一步当然是安装Git了.根据你当前使用的平台来阅读下面的文字: 1.1 在Linux上安装Git 首先,你可以试着输入git,看看系统有没有安装Git: ``` $ git The program 'git' is curren