MS RFC 98:标签/文本呈现大修

日期

2013/06

作者

托马斯堡

联系

thomas.bonfort@gmail.com

状态

采用

版本

MAPServer 7

1。现状

1.1文本呈现管道

当需要标记某个特性时,执行以下(简化)步骤:

  • 字符串最终通过iconv转换为utf8

  • 该字符串将字形从“逻辑顺序”重新排序为“视觉顺序”,以支持从右向左书写的语言。例如,假设大写字母是阿拉伯字母,则在要素中存储的文本为 this is some ARABIC text 被转化为 this is some CIBARA text 为了透明地提供给当前从左到右布局字形的呈现器

  • 字符串经过换行符,当前RTL脚本在我们呈现时进行换行符(for arabic n text )

    TXET
    CIBARA
    

    而不是所需的

    CIBARA
    TXET
    
  • 字符串经过对齐,这是目前黑客和不精确的(感谢您的忠实),因为我们(尝试)通过填充空格而不是每行使用精确偏移来实现对齐。

  • 然后,字符串按原样传递给渲染器,渲染器负责每个glyph的整个布局。

1.2个局限性

虽然目前的情况很容易理解,而且对于拉丁语来说效果也很好,但是这个RFC的目标是解决一些缺点:

  • 所有渲染器都在复制布局逻辑:

    • 将utf8字符串拆分为单个glyph

    • 在字体文件中查找给定的字形

    • 相对于前一个设置标志符号(这包括后续字符之间的间距,或确定遇到换行符时应垂直添加多少像素)

  • 虽然拉丁语言在字符串字符和字体glyph之间有一个1对1的映射,但对于复杂语言则不是这样,因为需要根据上下文插入连字glyph(请参见 wikipedia complex text layout page 更完整的解释)。虽然这部分由fribidi for arabic(它有一个为我们插入连接符的成形算法)负责,但我们目前在其他复杂的文本语言中很难做到。

  • 我们根本不支持双间距或行距,而且我们的文本对齐方式(中间、右侧)不精确,因为这些方法的实现需要添加到每个渲染器中。

2。拟大修

大局见 the state of text rendering .

简而言之,我们不是将文本字符串和起始位置传递给呈现器,而是传递字形列表(即字形是特定字体文件中的特定条目)以及它们的精确位置,这与我们当前渲染时所做的非常相似 FOLLOW labels, as is already outlined in bug 3611 。实际的形状和布局发生在单个映射服务器函数中,呈现器只是将字形静静地放置在它们被告知的位置。

2.1架构影响

在体系结构上,这种修改非常重要,因为需要重构整个标签呈现链,以便考虑到定位glyph列表,而不仅仅是纯文本字符串。

2.1.1依赖性

  • Freetype成为地图服务器的核心依赖项,因为我们需要比当前更高级别的字形大小信息(其中freetype是单个呈现器的依赖项)。我们还需要一个中央字形缓存来跨harfbuzz和渲染器使用。 fontsetObj 可能需要扩展为不仅仅是一个简单的文件名哈希表。

  • fribidi(它有一些线程安全问题)被保存为为为我们处理bidi算法的库。我们不再用它来塑形。

  • 引入harfbuzz以支持复杂的脚本变形(简化:在字符之间插入连接符的工具)。HarfBuzz算法将应用于已确定为非拉丁语的文本字符串(即,HarfBuzz导致的速度减慢仅限于那些实际需要整形的语言)。

  • uthash(http://troydhanson.github.io/uthash/)是一个只包含头的哈希表实现,可与任意键和值一起使用,并用于访问缓存的字体和glyph。考虑到一些性能测试,我们可能希望有一天用这个来替换我们自己的哈希表实现。

请注意,fribidi和harfbuzz依赖项仍然是可选的,并且可以在编译时对那些只处理拉丁脚本的人禁用。Harfbuzz和Fribidi需要一起启用或禁用。

2.1.2字体和字形缓存

(font cache.c)全局(如果需要,线程保护)glyph和字体缓存将确保缓存的glyph可在fastcgi案例的多个请求中重用,但反过来需要一些线程级保护,可能还需要一些修剪,以使其保持合理的大小。为了让fontcache可访问,一些API已经更改。fontcache包含以下缓存:

  • 字体(即TrueType文件的表示)

  • 字形(即给定大小的单个字形的度量)

2.1.2将文本字符串转换为定位glyph列表

此步骤将添加到MapServer,包括协调freetype、bidi算法、harfbuzz、换行、文本对齐以及未来的文本和行距的输出。这个步骤在很大程度上可以通过pango透明地实现,但是pango很难跨平台支持,据我所知,它对fontconfig有着严格的依赖性,这与我们的fontset方法不兼容。

文本由“textpathobj”表示,它基本上是定位glyph的列表。(例如,对于Arial字体,大小为10的单词“l a b e l”由位置(x,y)=(0,0)处的Arial字体的glyph“l”、位置(10,0)处的glyph“a”、位置(18,0)处的“b”等表示)。多行文本通过在不同的Y值处放置glyph透明地处理。文本路径可以是“绝对”(即glyph位置在绝对图像坐标中,用于定位角度跟随标签的glyph)或“相对”(在这种情况下,它们必须被其标签点偏移)。

所有的变形都发生在textlayout.c中,其主要角色是接受一个文本字符串作为输入,并返回一个定位glyph列表作为输出。输入字符串经过多个步骤,并被拆分为多个运行。每次运行都有一个不同的行号、bidi方向和脚本“language”。

例如,我们将使用输入的Unicode字符串“这是一些英语、阿拉伯语和日语文本”。大写字母用于表示非拉丁字母,还请注意,阿拉伯语按逻辑(=阅读)顺序存储,而它将呈现为cibara。

  • 将字符串转换为Unicode的ICONV编码转换

run1 = "this is some text in english, ARABIC and JAPANESE", line=0
  • 换行:换行符换行,空格换行

run1 = "this is some text in english,", line=0
run2 = "ARABIC and JAPANESE", line=1
  • bidi级别,每个运行都有一个bidi方向(即从左到右或从右到左)

run1 = "this is some text in english,", line=0, direction=LTR
run2 = "ARABIC" line=1, direction=RTL
run3 = " and JAPANESE", line=1, direction = LTR
  • 脚本检测应用于启用依赖于语言的形状,并优化将使用的字体(稍后将详细介绍)

run1 = "this is some text in english,", line=0, direction=LTR, script=latin
run2 = "ARABIC" line=1, direction=RTL, script=arabic
run3 = " and " line=1, direction=LTR, script=latin
run4 = "JAPANESE" line=1, direction=LTR, script=hiragana
  • 对于每个运行,我们选择应该使用的字体,以便在给定的运行中使用相同的字体。 MS RFC 80:字体回退支持 允许为一个标签指定多个字体,这已扩展到能够微调哪些字体最好用于给定脚本:

FONT "arialuni,arial,cjk,arabic"

现在可以用脚本标识符(即

  FONT "arialuni,en:arial,ja:cjk,ar:arabic"

This is needed as there is and will be overlap between font glyph
coverages, and it should be possible to prioritize which font is used
for which language.
  • 然后将每次运行输入harfbuzz,该函数返回定位glyph的列表。返回的glyph的数目并不意味着与Unicode字符串中的glyph数目相同,而是从左到右排列。作为加速,run who的脚本是“拉丁语”不是通过harfbuzz提供的,而是使用缓存的glyph高级。

  • 重新组装每个运行的标志符号,以说明行号和运行位置(例如,运行3向下偏移一行,并放置在运行2的右侧)。

  • 每一行水平地加上括号,以便于对齐。标签对齐现在停止默认为左对齐,因此从右向左的运行将是右对齐的,而不是现在的左对齐。

2.1.3渲染阶段处理glyph

需要更新labelcache和渲染器才能使用glyph列表。这里的变化是广泛的,但在概念上应该保持简单。单个渲染器被大大简化。

已尽可能减少labelcache计算:

将功能插入labelcache时:

  • 如果labelobj及其子StyleObj不包含任何绑定,我们将插入对原始labelobj的引用,而不是副本。当属性绑定不在使用时,这会减少内存使用的DOW。

  • 我们不会插入永远无法呈现的功能(例如,超出比例,对其功能而言太大(minFeatureSize关键字))。

在msdrawlabelcache阶段:

  • 我们将标签文本边界框的计算延迟到,在我们检查了可能导致它无法呈现的条件之后,即。

    • 如果他们有一个思维定势和一个相同文本的相邻标签已经呈现

    • 对于没有标记的标签,我们首先检查LabelPoint是否与现有标签冲突。

  • 碰撞检测已优化:

    • 我们保留一个呈现标签的列表,并遍历这些标签,而不是检查每个成员的labelcache中所有标签的状态。

    • 标签的边界度量已从完整的shapeobj缩减为包含边界rect和可选lineobj的结构。对于非旋转标签,不需要任何信息,只需要边界框,这使得这样的两个标签更容易进行交叉检测(即,这两个标签的重叠与边界框的重叠相同,无需进入更多的几何交叉基本体)。

    这些变化的加速对于杂乱的地图非常重要,c.f.https://plus.google.com/u/0/118271009221580171800/posts/prwhfyskhea(例如,500.000个标签的渲染时间从800秒到1秒)。

2.2向后兼容性

  • 在高(mapfile)级别,不需要。

  • 某些mapscript API可能会更改,或者可能需要打包。(TBD)

  • 角色位置的一些细微但广泛的变化。

  • MS RFC 99:删除对gd渲染器的支持 是这些变化的附带因素

2.3性能影响

可能很多:

  • 通过检测和缩短这些案例,仅拉丁语文本的现有性能需要保持在当前水平。如果做得正确,我们实际上可以加快拉丁文本渲染的速度。

  • 对于其他脚本,通过哈夫布兹的成本 will 带上一个性能惩罚。

三。其他

3.1问题跟踪ID

https://github.com/MapServer/MapServer/issues/4673

3.2投票历史

+Mikes、Stephanm、Tomk、Jeff、Stevew、Stevel和Thomasb的1份