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

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

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

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

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

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

редактировать

цель моего кода подсчитайте символы в тексте Юникода, предполагая, что лигатура является одним символом. Поэтому мне нужен способ свернуть несколько кодовых точек в одну лигатуру.

5 ответов


на Компьютерная Верстка страница в Википедии написано -

компьютер современный Римский шрифт обеспеченный с TeX включает 5 общие лигатуры ff, fi, fl, ffi и ФФЛ. Когда TeX находит эти комбинации в тексте он заменяет соответствующая лигатура, если только заменен наборщиком.

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

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

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

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

  1. баг хрома http://code.google.com/p/chromium/issues/detail?id=22240
  2. ошибка Firefox https://bugs.launchpad.net/firefox / + ошибка / 37828

вы говорите не о лигатурах (по крайней мере, не на языке Юникода), а о кластерах графем. Существует стандартное приложение, которое связано с обнаружением границ текста, включая границы кластера графем:

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

Также см. Описание адаптированных кластеров графем в regular выражения:

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

и определение графем сортировки:

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

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


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

для данной строки экземпляр шрифта может создать 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.


пока Аарона!--5--> не совсем правильно, это подтолкнуло меня в правильном направлении. После прочтения документов 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))))))))))

также написал по сути.


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

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

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