在 Clojure/Java 中检测 Unicode 文本连字
题
连字是由多个代码点表示的 Unicode 字符。例如,天城文 त्र
是由代码点组成的连字 त + ् + र
.
当在记事本等简单文本文件编辑器中看到时, त्र
显示为 त् + र
并存储为三个 Unicode 字符。但是,当在 Firefox 中打开同一文件时,它会显示为正确的连字。
所以我的问题是,如何在从我的代码读取文件时以编程方式检测此类连字。既然 Firefox 做到了这一点,那么就必须存在一种以编程方式完成它的方法。是否有任何 Unicode 属性包含此信息,或者我是否需要拥有所有此类连字的映射?
SVG CSS 属性 text-rendering
当设置为 optimizeLegibility
做同样的事情(将代码点组合成正确的连字)。
附:我正在使用Java。
编辑
我的代码的目的是计算 Unicode 文本中的字符数,假设连字是单个字符。所以我需要一种将多个代码点折叠成单个连字的方法。
解决方案 4
虽然阿龙的回答是不完全正确的,它把我推在正确的方向。通过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))))))))))
也贴上主旨。
其他提示
这 电脑排版 维基百科页面说 -
TEX提供的计算机现代罗马字体包括五个常见的连接FF,FI,FL,FFI和FFL。当Tex在文本中找到这些组合时,除非被排版的人覆盖,否则将取代适当的绑扎。
这表明是编辑器进行了替换。而且,
Unicode坚持认为,连接是一个演示问题,而不是字符定义问题,例如,“如果要求现代字体显示'h',然后是'r',并且字体中有一个'HR'连接,它可以显示绑扎。”
据我所知(我对这个主题有一些兴趣,现在刚刚阅读了几篇文章),连字替代的说明嵌入在字体中。现在,我深入研究并为您找到了这些; GSUB - 字形替换表 和 连字替换子表 来自 OpenType 文件格式规范。
接下来,您需要找到一些可以让您在 OpenType 字体文件中达到峰值的库,即用于快速访问的文件解析器。阅读以下两篇 讨论 可能会给您一些如何进行这些替换的指导:
你所说的不是连字(至少不是 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 关心纯粹的表示细节,而不是字符或字素簇语义,但排序算法和定制的字素簇边界查找算法看起来好像它们可以独立于字体来实现。
您也许可以从 GlyphVector 类获取此信息。
对于给定的 String,Font 实例可以创建一个 GlyphVector,它可以提供有关文本呈现的信息。
这 布局GlyphVector() Font 上的方法可以提供此功能。
这 FLAG_COMPLEX_GLYPHS GlyphVector 的属性可以告诉您文本是否与输入字符没有 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。
我觉得那是你真正需要的是Unicode Normalization
。
有关Java中,你应该检查的http:// download.oracle.com/javase/6/docs/api/java/text/Normalizer.html
通过选择合适的范式,你可以得到你所期待的。