在Clojure / Java中检测Unicode文本连字

时间:2010-08-12 10:05:55

标签: java unicode text clojure ligature

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

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

所以我的问题是,如何从我的代码中读取文件时以编程方式检测此类连字。自从Firefox成功以来,必须有一种以编程方式完成它的方法。是否有任何包含此信息的Unicode属性,或者我是否需要为所有此类连字创建映射?

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

PS:我正在使用Java。

修改

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

5 个答案:

答案 0 :(得分:2)

Computer Typesetting维基百科页面上写着 -

  

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

这表明它是编辑器进行替换。而且,

  

Unicode认为绑定是   一个表达问题而不是一个   字符定义问题,那,   例如,“如果现代字体是   要求显示'h'后跟'r',   并且字体中有'hr'连字符   它,它可以显示结扎。“

据我所知(我对这个主题感兴趣,刚刚阅读了几篇文章),结合替换的说明嵌入了字体内。现在,我挖了更多,为你找到了这些; OpenType文件格式规范中的GSUB - The Glyph Substitution TableLigature Substitution Subtable

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

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

答案 1 :(得分:2)

你所谈论的不是连字(至少不是用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关注的是纯粹的表示细节,而不是字符或字素集群语义,但是整理算法和定制的字形集群边界查找算法看起来好像可以独立于字体实现。

答案 2 :(得分:1)

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

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

Font上的layoutGlyphVector()方法可以提供此功能。

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

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

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。

答案 3 :(得分:1)

虽然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

答案 4 :(得分:0)

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

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

通过选择正确的规范化表格,您可以获得所需的内容。