[Trennmuster] Skript zum Erstellen einer Wortliste für Ligatursatzmuster

Lukas Sommer sommerluk at gmail.com
So Aug 14 17:56:31 CEST 2016


Hallo zusammen.

1.)
Dass Listen für die Haupttrennstellen bereits mit „make“ erzeugt
werden können, war mir gar nicht bewusst. Danke für den Hinweis!
Schlussendlich habe ich das Node.js-Skript trotzdem nach Python
portiert. Einerseits glaube ich, dass es durchaus praktisch sein kann,
für die Ligaturmuster ein eigenes Skript zu haben. So können
Feinjustierungen vorgenommen werden, ohne dass bestehende Anwendungen
dadurch berührt würden. Andererseits hatte ich einen etwas anderen
Ansatz gewählt: nur ein einziges Ligaturmuster für alle Varianten
(alte/neue Rechtschreibung, deutsche/schweizer S-Regel). Das geht auch
ganz gut. Das Skript arbeitet folgendermaßen:

‣ Uneindeutige Einträge (= alle Eintrage, die „[“ enthalten) werden
einfach ignoriert.
‣ Unklassifizierte Einträge (= alle Einträge, die U+00B7 MIDDLE DOT
„•“ enthalten) werden einfach ignoriert.
‣ Inkonsistente Einträge (= alle Einträge, die nicht in allen
Rechtschreibvarianten dieselben Positionen für „<“, „>“ und „=“ haben)
werden einfach ignoriert.
‣ Alle anderen Einträge werden verwendet.

Dabei werden in der Praxis nur sehr wenige Einträge ignoriert. Für die
aktuelle Wortliste habe ich die Log-Datei angehängt. Die
inkonsistenten Einträge sind lediglich ein paar Zahlwörter und das
Wort „sodass“. Deshalb glaube ich, dass man mit nur einem einzigen
Muster gut alle Varianten abdecken kann.

2.)
Das Ziel des Skripts ist es, eine einfach zu handhabende Lösung für
Scribus anzubieten, die standardkonform ist und wirklich alles
abdeckt. Daher bietet es sich an, mit nur einem Muster alle
Sprachvarianten und alle theoretisch denkbaren Ligaturen (auch
Schmuckligaturen und Schreibschriften und Fraktur) abzudecken – einige
Schriftarten bieten tatsächlich sehr ungewöhnliche Ligaturen. Das neue
Scribus bewegt den Textcursor nach Graphem-Clustern, was das Arbeiten
mit dem Bindehemmer einigermaßen komfortabel macht. Dass einige andere
Programme (Texteditoren, Word …) den Bindehemmer teilweise fehlerhaft
interpretieren, stört mich da wenig. Davon abgesehen hat auch Scribus
selbst noch Schwierigkeiten bei der Rechtschreibprüfung und der
Worttrennung, wenn Bindehemmer vorhanden sind. (Übrigens gibt es wohl
tatsächlich eine ISO-Norm, die ein Ersatzzeichen für den Bindehemmer
definiert. Es ist auf Wikipedia
[https://de.wikipedia.org/wiki/Bindehemmer] zu bestaunen.

3.)
> Zwischen Beugungsendungen (-te, -ten) und
> Endsilben (-lein, -lich, -los) und dem restlichen
> Wortteil werden ebenfalls keine Ligaturen
> gesetzt. Bsp.: ich haf>te, Eltern haf>ten,
> Häuf>lein, höf>lich, mittel>los
Im Moment hat die Wortliste allerding nicht „haf>te“, sondern
„haf-te“. Bei Beugungsendungen wird also momentan die Ligatur nicht
unterdrückt. Mich stört das nicht. Wenn man „er kauft“ mit Ligatur
setzt (die Beugungsendung „t“ ist nicht durch einen Bindehemmer
abgetrennt, aber auch keine eigene Silbe), dann halte ich auch „er
kaufte“ mit Ligatur für vertretbar …

4.)
> An welcher Stelle fügst du die Bindehemmer
> ein? Bietet Scribus Zugriff auf interne
> Datenstrukturen oder änderst du die
> Eingabedatei?
In der Tat bietet Scribus Zugriff auf interne Datenstrukturen. In
Scribus-Dokumenten gibt es Textrahmen. Denen liegt ein Textinhalt zu
Grunde, der „Story“ genannt wird. Es können sich auch mehrere
verkettete Textrahmen dieselbe Story teilen. Scribus bietet vollen
Zugriff auf diese Datenstruktur. (Die Daten sind in UTF16 kodiert. Der
Index zählt nicht die Zeichen, sondern die UTF16-Codeunits. Alle
Operationen beziehen sich jedoch auf ganze Zeichen und nicht auf halbe
Surrogate-Paare – das führt schon mal zu etwas seltsamen Resultaten.)

-- 
Lukas Sommer
-------------- nächster Teil --------------
#!/usr/bin/env python
# -*- coding: utf8 -*-


# This script is for Python 2.7


import codecs
import re
import argparse


def make_patgen_input():
    parser = argparse.ArgumentParser(
        description="Make patgen input for ligature patterns.")
    parser.add_argument(
        'input',
        help="Input wordlist (UTF8 encoded) in "
             "the file format of the Trennmuster project.")
    parser.add_argument(
        'output',
        help="Output wordlist (UTF8 encoded) as "
             "simple word list with a dash that marks all positions where "
             "ligatures have to be suppressed. This file can be used as "
             "input for patgen. Note however that it is UTF8 encoded, "
             "therefor you have to convert it to a single-byte encoding "
             "that patgen understands (e.g. ISO 8859-15) before using it "
             "in patgen. Attention: Existing files will be overwritten.")
    parser.add_argument(
        'log',
        help="log (UTF8 encoded) of the operations. "
             "Attention: Existing files will be overwritten.")
    args = parser.parse_args()

    # open the files
    wordlist_file = codecs.open(args.input, "rb", "UTF8", "strict")
    patgen_file = codecs.open(args.output, "wb", "UTF8", "strict")
    log_file = codecs.open(args.log, "wb", "UTF8", "strict")

    # initialize variables for log data
    double_meaning = []
    unclassified = []
    inconsistent = []

    # process data from the word list
    print u"Please wait while processing data..."
    for line in wordlist_file:
        # remove comments
        simplified_line = line.split(u"#")[0]
        # test for double meaning
        if u"[" in simplified_line:
            double_meaning.append(line)
            continue
        # test for non-classified things: U+00B7 MIDDLE DOT
        if u"·" in simplified_line:
            unclassified.append(line)
            continue
        # remove whitespace
        simplified_line = simplified_line.strip()
        # remove information about special hyphenations following 1901
        # orthography (Not useful because most programs makes the
        # hyphenation automatically, but scripts access normally to the
        # non-hyphenated underlying data.)
        # We use *? instead of *, because * is greedy,
        # but *? is reluctant/non-greedy, which makes sure that we do not
        # match various blocks at the same time.
        simplified_line = re.sub(
            ur"(\{)(.*?)/.*?\}",
            ur"\2",
            simplified_line)
        # remove placeholder content of empty fields (like “-2-” for
        # an empty field at column 2)
        simplified_line = re.sub(
            ur"\-[0-9]\-",
            ur"",
            simplified_line)
        # remove hyphenations within morphemes (not useful for
        # ligature setting)
        simplified_line = simplified_line.replace(u"-", u"")
        # remove markers for bad hyphenation quality
        # (not useful for ligature setting)
        simplified_line = simplified_line.replace(u".", u"")
        # use “-” as sign for morpheme boundaries (“<”, “>”, “=”)
        # (“-” is guaranteed to not occur in the string, because it is
        # yet filtered out), which will later also be recognized by patgen.
        simplified_line = simplified_line.replace("<", "-")
        simplified_line = simplified_line.replace(">", "-")
        simplified_line = simplified_line.replace("=", "-")
        # remove duplicate morpheme boundaries
        while simplified_line.count("--") > 0:
            simplified_line = simplified_line.replace("--", "-")
        # transform into an array
        my_array = simplified_line.split(u";")
        # remove the first column (does not contain ligature data,
        # but only the non-hyphenated word)
        if len(my_array) > 0:
            my_array.pop(0)
        # remove empty fields
        while u"" in my_array:
            my_array.remove(u"")
        # nothing to do if there is no data (like on empty lines)
        if len(my_array) == 0:
            continue
        # test if there are differences between the remaining fields
        if len(my_array) != my_array.count(my_array[0]):
            inconsistent.append(line)
            continue
        # write to file
        patgen_file.write(my_array[0].lower() + u"\n")

    # close wordlist_file and patgen_file
    wordlist_file.close()
    patgen_file.close()

    # write log
    log_file.write(
        u"The following entries "
        u"with ambiguous morpheme boundaries "
        u"have been ignored:\n")
    for entry in double_meaning:
        log_file.write(entry)
    log_file.write(
        u"\n\nThe following entries "
        u"with unclassified hyphenation points "
        u"have been ignored:\n")
    for entry in unclassified:
        log_file.write(entry)
    log_file.write(
        u"\n\nThe following entries "
        u"with inconsistencies between different orthographies "
        u"have been ignored:\n")
    for entry in inconsistent:
        log_file.write(entry)

    # close log file
    log_file.close()

    # exit
    print("Done.")


make_patgen_input()
-------------- nächster Teil --------------
Ein Dateianhang mit Binärdaten wurde abgetrennt...
Dateiname   : log
Dateityp    : application/octet-stream
Dateigröße  : 7658 bytes
Beschreibung: nicht verfügbar
URL         : <https://listi.jpberlin.de/pipermail/trennmuster/attachments/20160814/dade496f/attachment.obj>


Mehr Informationen über die Mailingliste Trennmuster