一个成熟应用的排版方案

这篇博客的初衷

我是负责一个阅读app的阅读器部分的,碰到过很多问题,每次想在网上找到成熟的解决方案时,都没有,没有,没有。。。网上的几乎都是demo,demo的问题就是考虑的问题不够全面,所以demo的方法我是用不了的。

因为深知痛苦,所以决定写出来我们应用中的方案,当然如果能达到抛砖引玉的效果就更更好了~~

适用范围

我们app中纯文本和ePub格式的绘制方法是分开的,本文只讨论纯文本。而且我不会在文中解释Core Text的基础概念,需要读者对Core Text事先有些了解。

第一版

文字内容的绘制有很多方法,比如用TextKit,甚至还有些产品定位更简单的,就直接用UILabel的。

我们app里采用的Core Text的解决方案。Core Text最简单的使用方式:

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)_contentStr);
            CGPathRef path = [UIBezierPath bezierPathWithRect:rectForDraw].CGPath;
            CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
            CFRelease(framesetter);
            CTFrameDraw(frame, context);

第二版

问题

Core Text在处理文字内容同时包含多种不同体系的语言的时候可能会出现问题。比如如下:一段文字中同时有中英文emoji,造成行间距时大时小。(从别的地方扣的图嘿嘿)第一张图是用CTFrame绘制出来的效果,第二张图是理想中的效果。

解决办法

造成这个问题的原因,可以参考http://www.minroad.com/?p=776

解决办法就是一行一行画,这样可以自己计算每行理想的位置,并在理想的位置绘制。

以下是绘制行的代码,至于怎么计算每行的位置,在上面推荐的文中有答案。

for (NSInteger i = 0; i < CFArrayGetCount(_ctvLines); i++) {
            CTLineRef line = CFArrayGetValueAtIndex(_ctvLines, i);
            CGPoint origin = _ctvLineOrigins[i];
            CGContextSetTextPosition(context, origin.x, origin.y);
            CTLineDraw(line, context);
        }

第三版

问题

中文内容的排版中,几乎唯一例外都使用的是左右对齐,只有这样才美观,但这在英文环境下就有问题了。

英文内容的绘制中,当在换行位置有个较长的单词,就会导致该行单词的字母间距也会被拉大:

以下是QQ阅读的效果:

以下是掌阅的效果:

解决办法

这个问题的解决办法我找了很久,stackoverflow上也有不少这样的问题,但是提出的解决办法,都不适用我们app。下面是stackoverflow上找到的方法:

  • 用WebKit,直接就完美。直接pass
  • 使用连字符的排版。这个是英文最完美的排版方式,但在实现中我发现,要想用连字符,必须!必须!使用CTFrameDraw方法,不能一行一行地绘制。得到CTFrame后,取出CTFrame里的CTLine,改变位置,一行一行地绘制,也不可以-_-||
  • 还有还有忘了。。。原谅我。。。

我的思路分两个方向:

  • Core Text有没有什么属性可以设置?
  • 一个字形一个字形地绘制

第一种方法:我找了CFAttributeString的所有属性,没有!

但发现了SFNTLayoutTypes.h和SFNTTypes.h。据文档说,应该是TrueType的特性的声明,但是找不到任何可以使用这些枚举的地方。而且我对TrueType太不了解,所以放弃。如果大家有了解的,或者看过什么这方面文章的,可以告诉我哈~

第二种方法:

一个CTLine(行)包含一个或多个CTRun(一段属性相同的字形)。翻阅CTRun的方法就会发现,没有提供改变run中每一个glyph字形位置的方法,而且glyph是不能像CTFrame, CTLine, CTRun直接绘制的,因为它是字符集中的概念,不是可绘制的。所以这个方法也不能达到效果。

没有办法中的方法,我想到了这个方法:

计算当前行所有字形的总宽度,如果与绘制面板的宽度差值过大,就左对齐,不再左右对齐。效果图如下:

但是,这种方式也有负面作用,但是还可以接受。如下:

根据表象来推测,QQ阅读也是采用这种方式的,嘎嘎嘎~掌阅牛轰轰啊!它是自己搞得文字绘制引擎吗?

推荐

研究问题的过程中发现了几篇不错的文章,低调奢华有内涵,推荐给大家:

https://www.raizlabs.com/dev/2015/08/advanced-ios-typography/

http://fontforge.github.io/en-US/documentation/interface/justify/

http://www.cnblogs.com/wycg1984/archive/2010/06/21/1762148.html

posted @ 2016-11-04 15:35  小Garfield  阅读(790)  评论(0编辑  收藏  举报