【问题标题】:Detecting Unicode text ligatures in Clojure/Java在 Clojure/Java 中检测 Unicode 文本连字
【发布时间】:2010-08-12 10:05:55
【问题描述】:

连字是由多个代码点表示的 Unicode 字符。例如,在梵文中,त्र 是一个由代码点 त + ् + र 组成的连字。

当在记事本等简单的文本文件编辑器中看到时,त्र 显示为त् + र,并存储为三个 Unicode 字符。但是,当在 Firefox 中打开同一个文件时,它会显示为正确的连字。

所以我的问题是,如何在从我的代码中读取文件时以编程方式检测此类连字。既然 Firefox 做到了,就必须存在一种以编程方式做到这一点的方法。是否有任何包含此信息的 Unicode 属性,或者我是否需要映射到所有此类连字?

SVG CSS 属性text-rendering 设置为optimizeLegibility 时执行相同的操作(将代码点组合成正确的连字)。

PS:我正在使用 Java。

编辑

我的代码的目的是计算 Unicode 文本中的字符数,假设连字是单个字符。所以我需要一种方法将多个代码点折叠成一个连字。

【问题讨论】:

  • 你能解释一下你为什么需要它吗?我问这个是因为没有简单的方法可以做到这一点。
  • @SorinSbarnea:我在帖子中添加了目的。
  • 我认为单独实现复杂的脚本处理希望不大。使用现有的库之一,例如 Uniscribe 或 libfreetype。

标签: java unicode text clojure ligature


【解决方案1】:

Computer Typesetting 维基百科页面说 -

计算机现代罗马字体 TeX 提供的包括五个 常用连字 ff、fi、fl、ffi 和 ffl。当 TeX 找到这些组合时 在文本中它代替 适当的结扎,除非 被排版器覆盖。

这表明它是编辑器进行替换。此外,

Unicode 认为连字是 演示问题而不是 字符定义问题,以及, 例如,“如果现代字体是 要求显示“h”后跟“r”, 并且字体在 它,它可以显示连字。”

据我所见(我对这个主题产生了一些兴趣,并且刚刚阅读了几篇文章),连字替换的说明嵌入在字体中。现在,我深入挖掘并为您找到了这些; GSUB - The Glyph Substitution TableLigature Substitution Subtable 来自 OpenType 文件格式规范。

接下来,您需要找到一些可以让您在 OpenType 字体文件中达到峰值的库,即用于快速访问的文件解析器。阅读以下两个讨论可能会给您一些关于如何进行这些替换的指导:

  1. 铬错误http://code.google.com/p/chromium/issues/detail?id=22240
  2. Firefox 错误https://bugs.launchpad.net/firefox/+bug/37828

【讨论】:

  • 看起来不错。我将浏览文章和错误补丁代码并尝试找到解决方案。
【解决方案2】:

您也许可以从 GlyphVector 类中获取此信息。

对于给定的 String,Font 实例可以创建一个 GlyphVector,该 GlyphVector 可以提供有关文本呈现的信息。

Font 上的layoutGlyphVector() 方法可以提供这个。

GlyphVector 的FLAG_COMPLEX_GLYPHS 属性可以告诉您文本是否没有与输入字符的一对一映射。

以下代码显示了一个示例:

JTextField textField = new JTextField();
String textToTest = "abcdefg";
FontRenderContext fontRenderContext = textField.getFontMetrics(font).getFontRenderContext();

GlyphVector glyphVector = font.layoutGlyphVector(fontRenderContext, textToTest.toCharArray(), 0, 4, Font.LAYOUT_LEFT_TO_RIGHT);
int layoutFlags = glyphVector.getLayoutFlags();
boolean hasComplexGlyphs = (layoutFlags & GlyphVector.FLAG_COMPLEX_GLYPHS) != 0;
int numberOfGlyphs = glyphVector.getNumGlyphs();

numberOfGlyphs 应该表示用于显示输入文本的字符数。

不幸的是,您需要创建一个 java GUI 组件来获取 FontRenderContext。

【讨论】:

  • 不起作用。 hasComplexGlyphs 为真,但 numberOfGlyphs 返回与 unicode 文本长度相同的数字。
【解决方案3】:

您所说的不是连字(至少不是 Unicode 用语),而是字形簇。有一个标准附件涉及发现文本边界,包括字素簇边界:

http://www.unicode.org/reports/tr29/tr29-15.html#Grapheme_Cluster_Boundaries

另见正则表达式中定制字素簇的描述:

http://unicode.org/reports/tr18/#Tailored_Graphemes_Clusters

以及排序规则字素的定义:

http://www.unicode.org/reports/tr10/#Collation_Graphemes

我认为这些都是起点。更难的部分可能是找到适用于梵文语言环境的 Unicode 排序算法的 Java 实现。如果你找到了,你可以分析字符串,而不用求助于 OpenType 特性。这会更简洁一些,因为 OpenType 关注的是纯粹的表现细节,而不是字符或字素簇语义,但整理算法和定制的字素簇边界查找算法看起来好像可以独立于字体实现。

【讨论】:

  • 这不是字素簇。 Ligutures是几个字素(字素簇)的组合。上面的例子是一个由两个簇组成的连字,第一个是两个字符,第二个是一个字符。我已经实现了 UAX-29,这里是集群:第一个是双字符 त्(以 virama 组合标记结尾),第二个是单字符 र
【解决方案4】:

虽然Aaron's answer 并不完全正确,但它把我推向了正确的方向。在阅读了java.awt.font.GlyphVector 的 Java API 文档并在 Clojure REPL 上玩了很多之后,我能够编写一个函数来满足我的需求。

这个想法是在glyphVector 中找到字形的宽度,并将宽度为零的字形与最后找到的非零宽度字形组合起来。解决方案在 Clojure 中,但如果需要,它应该可以翻译成 Java。

(ns net.abhinavsarkar.unicode
  (:import [java.awt.font TextAttribute GlyphVector]
           [java.awt Font]
           [javax.swing JTextArea]))

(let [^java.util.Map text-attrs {
        TextAttribute/FAMILY "Arial Unicode MS"
        TextAttribute/SIZE 25
        TextAttribute/LIGATURES TextAttribute/LIGATURES_ON}
      font (Font/getFont text-attrs)
      ta (doto (JTextArea.) (.setFont font))
      frc (.getFontRenderContext (.getFontMetrics ta font))]
  (defn unicode-partition
    "takes an unicode string and returns a vector of strings by partitioning
    the input string in such a way that multiple code points of a single
    ligature are in same partition in the output vector"
    [^String text]
    (let [glyph-vector 
            (.layoutGlyphVector
              font, frc, (.toCharArray text),
              0, (.length text), Font/LAYOUT_LEFT_TO_RIGHT)
          glyph-num (.getNumGlyphs glyph-vector)
          glyph-positions
            (map first (partition 2
                          (.getGlyphPositions glyph-vector 0 glyph-num nil)))
          glyph-widths
            (map -
              (concat (next glyph-positions)
                      [(.. glyph-vector getLogicalBounds width)])
              glyph-positions)
          glyph-indices 
            (seq (.getGlyphCharIndices glyph-vector 0 glyph-num nil))
          glyph-index-width-map (zipmap glyph-indices glyph-widths)
          corrected-glyph-widths
            (vec (reduce
                    (fn [acc [k v]] (do (aset acc k v) acc))
                    (make-array Float (count glyph-index-width-map))
                    glyph-index-width-map))]
      (loop [idx 0 pidx 0 char-seq text acc []]
        (if (nil? char-seq)
          acc
          (if-not (zero? (nth corrected-glyph-widths idx))
            (recur (inc idx) (inc pidx) (next char-seq)
              (conj acc (str (first char-seq))))
            (recur (inc idx) pidx (next char-seq)
              (assoc acc (dec pidx)
                (str (nth acc (dec pidx)) (first char-seq))))))))))

还发布了on Gist

【讨论】:

    【解决方案5】:

    我认为你真正要找的是Unicode Normalization

    对于 Java,您应该检查 http://download.oracle.com/javase/6/docs/api/java/text/Normalizer.html

    通过选择适当的规范化形式,您可以获得所需的内容。

    【讨论】:

    • 不起作用。所有规范化模式都返回与输入相同的文本。
    • 规范化在代码点和 Unicode 等价关系级别上起作用,并且没有字素簇的概念。
    猜你喜欢
    • 2023-04-05
    • 2010-12-13
    • 1970-01-01
    • 2013-02-17
    • 2013-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-02
    相关资源
    最近更新 更多