Обнаружение текстовых лигатур Unicode в Clojure / Java - PullRequest
6 голосов
/ 12 августа 2010

Лигатуры - это символы Unicode, которые представлены более чем одной кодовой точкой.Например, в Деванагари त्र - это лигатура, состоящая из кодовых точек त + ् + र.

При просмотре в простых редакторах текстовых файлов, таких как Блокнот, त्र отображается как त् + र и сохраняется в виде трех символов Юникода.Однако, когда тот же файл открывается в Firefox, он отображается как правильная лигатура.

Поэтому мой вопрос заключается в том, как программно обнаруживать такие лигатуры при чтении файла из моего кода.Поскольку Firefox делает это, должен существовать способ сделать это программно.Существуют ли какие-либо свойства Unicode, которые содержат эту информацию, или мне нужно иметь карту для всех таких лигатур?

Свойство SVG CSS text-rendering, когда установлено optimizeLegibility, делает то же самое(объединить кодовые точки в правильную лигатуру).

PS: я использую Java.

РЕДАКТИРОВАТЬ

Цель моего кода - подсчитатьсимволы в тексте Unicode, предполагающие, что лигатура представляет собой один символ.Поэтому мне нужен способ свести несколько точек кода в одну лигатуру.

Ответы [ 5 ]

2 голосов
/ 12 августа 2010

То, о чем вы говорите, это не лигатуры (по крайней мере, не на языке 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

Я думаю, что это отправные точки. Сложнее всего, вероятно, будет найти Java-реализацию алгоритма сопоставления Unicode, который работает для локалей деванагари. Если вы найдете его, вы можете анализировать строки, не прибегая к функциям OpenType. Это было бы немного чище, поскольку OpenType имеет дело с чисто презентационными деталями, а не с семантикой кластера символов или графем, но алгоритм сопоставления и специализированный алгоритм поиска границ кластера графем выглядят так, как будто они могут быть реализованы независимо от шрифтов.

2 голосов
/ 12 августа 2010

На странице Computer Typesetting говорится: -

Гарнитура Computer Modern Roman, поставляемая с TeX, включает в себя пять лигатур ff, fi, fl, ffi и ffl.Когда TeX находит эти комбинации в тексте, он заменяет соответствующую лигатуру, если она не переопределена наборщиком.

Это указывает на то, что подстановка выполняет редактор.Более того,

Unicode утверждает, что лигатура - это проблема представления, а не проблема определения символа, и что, например, «если современный шрифт запрашивается для отображения« h », за которым следует« r »,и у шрифта есть лигатура 'hr', он может отображать лигатуру. "

Насколько я вижу (я заинтересовался этой темой и только сейчас читаю несколько статей),инструкция по замене лигатуры встроена в шрифт.Теперь, я копался в большем количестве и нашел это для вас; GSUB - Таблица подстановок глифов и Подстановочная таблица подстановок лигатур из спецификации формата файлов OpenType.

Далее вам нужно найти библиотеку, которая может позволить вам достичь пика внутриФайлы шрифтов OpenType, т.е. анализатор файлов для быстрого доступа.Чтение следующих двух обсуждений может дать вам несколько советов, как сделать эти замены:

  1. Ошибка хрома http://code.google.com/p/chromium/issues/detail?id=22240
  2. Ошибка Firefox https://bugs.launchpad.net/firefox/+bug/37828
1 голос
/ 13 августа 2010

Хотя ответ Аарона не совсем правильный, он подтолкнул меня в правильном направлении. Прочитав документацию по Java API java.awt.font.GlyphVector и много поиграв на 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))))))))))

Также опубликовано в Gist .

1 голос
/ 12 августа 2010

Вы можете получить эту информацию из класса GlyphVector.

Для данного String экземпляр Font может создать GlyphVector, который может предоставлять информацию о рендеринге текста.

Это может обеспечить метод layoutGlyphVector () в шрифте.

Атрибут 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.

0 голосов
/ 12 августа 2010

Я думаю, что вы действительно ищете Unicode Normalization.

Для Java вы должны проверить http://download.oracle.com/javase/6/docs/api/java/text/Normalizer.html

Выбрав соответствующую форму нормализации, вы можете получить то, что вы ищетедля.

...