准备知识
字符(Character)和字形(Glyphs):排版过程中一个重要的步骤就是从字符到字形的转换,字符表示信息本身,而字形是它的图形表现形式。字符一般就是指某种编码,如Unicode编码,而字形则是这些编码对应的图片。但是他们之间不是一一对应关系,同个字符的不同字体族,不同字体大小,不同字体样式都对应了不同的字形。而由于连写(Ligatures)的存在,多个字符也会存在对应一个字形的情况。
正文
CTLine
里边可能有一个或者多个CTRun
,那么CTRun
是怎么划分的?
刚开始学些 Core Text 看到这张图,就单纯的以为:一个CTFrame
对象对应一个自然段,一个CTLine
对应一行,一个CTRun
对应一个字。 错的理所当然。CTRun
不是一个字,CTFrame
也不是一个一篇文章中的自然段。
先看Core Text Programming Guide对于CTRun的解释:
Each CTLine object contains an array of glyph run (CTRun) objects. A glyph run
is a set of consecutive glyphs that share the same attributes and direction. The
typesetter creates glyph runs as it produces lines from character strings,
attributes, and font objects. This means that a line is constructed of one or more
glyphs runs. Glyph runs can draw themselves into a graphic context, if desired,
although most clients have no need to interact directly with glyph runs.
中文:
每个 CTLine 对象包含一个 glyph run(CTRun) objects的数组。一个 glyph run(CTRun)对象 是一系列共享相同
attributes 和 direction 的连贯的字形集合。当排版从字符、attributes、font objects 里生成 lines 时,
同时生成glyph runs(CTRun)。意思是一个line由一个或者更多glyphs runs(CTRun)组成。Glyph runs可以把它们自己绘制到 graphic context
上,如果你想着么做的话,然而大部分客户端不需要直接和glyph runs交互。
由上可知:每个CTLine
对象包含了一个CTRun
的数组,每个CTRun
又包含了一个字形集合(set)
CTLine
-> [CTRun
]
CTRun
-> [字形]
(字形的表示类在Core Graphics中定义:CGGlyph
)
可以通过CTRun.h
里定义的方法获取CTRun
对象中的字形集合:
/*!
@function CTRunGetGlyphs
@abstract Copies a range of glyphs into user-provided buffer.
@param run
The run whose glyphs you wish to copy.
@param range
The range of glyphs to be copied, with the entire range having a
location of 0 and a length of CTRunGetGlyphCount. If the length
of the range is set to 0, then the operation will continue from
the range's start index to the end of the run.
@param buffer
The buffer where the glyphs will be copied to. The buffer must be
allocated to at least the value specified by the range's length.
*/
void CTRunGetGlyphs(
CTRunRef run,
CFRange range,
CGGlyph buffer[_Nonnull] ) CT_AVAILABLE(macos(10.5), ios(3.2), watchos(2.0), tvos(9.0));
此时这个字形(CGGlyph
)才能对应一个字(字符)
那么如何将一个CTLine
表示的字符串划分为一个或者多个CTRun
呢?
CTRun
的划分
上文文档重点: the same attributes and direction
Cote Text的工作流程是:
通过 CFAttributedStringRef(NSAttributedString)
对象生成CTFramesetterRef
对象,通过CTFramesetterRef
对象生成CTFrameRef
对象,CTFrameRef
对象包含了CTLineRef
对象,CTLineRef
对象包含了CTRunRef
对象
CFAttributedStringRef(NSAttributedString)
对象是数据源,可以理解为是带有字号,颜色等属性信息的字符串,NSAttributedString
的可变类型NSMutableAttributedString
,我们可以添加删除替换属性信息。在一个 range
范围内的子字符串的属性内容相同并且被包含在一个CTLine
内,则就会划分到同一个CTRun
对象中。
NSAttributedString
属性被包含在一个字典中NSDictionary<NSAttributedStringKey, id>
,当设置属性的时候可以指定range
范围,例如设置前两个字符的颜色为红色:
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 2)];
观察如下代码:
NSMutableAttributedString *one = [[NSMutableAttributedString alloc] initWithString:@"Shadow"];
NSLog(@"%@",one);
[one addAttribute:@"123" value:@"123" range:NSMakeRange(0, 2)];
[one addAttribute:@"456" value:@"456" range:NSMakeRange(2, 2)];
NSLog(@"%@",one);
打印结果:
2018-10-27 18:27:34.947284+0800 TextDemo[13804:363305] Shadow{
}
2018-10-27 18:27:34.947526+0800 TextDemo[13804:363305] Sh{
123 = 123;
}ad{
456 = 456;
}ow{
}
可以看到不同范围内有不同的属性,在数据源NSAttributedString
中要展示的字符串就会被分割成多个不同的区间,这些区间就会对应生成不同的CTRun
对象。如果一个NSAttributedString
对象所有字符都有相同的属性并且能在一行内展示完,那么这个NSAttributedString
对象会对应生成一个CTLine
对象,并且这个CTLine
对象中只包含一个CTRun
对象。
那么这个CTRun
对象里边包含几个字形(CGGlyph
)对象呢?这个就不好说了,这取决于字符串包含的字符数、字体、字号等信息。