David Maus M. A.

Digital Humanities Publishing

Zotero Standalone, Revisited

David Maus, 29.12.2018 · Permalink

Letztes Jahr habe ich das Setup beschrieben, mit dem ich auf meinen Systemen Zotero ohne Abhängigkeit von DBUS und GTK+ 3 installiere. Ein Jahr später habe ich die Weihnachtstage genutzt, um das Setup mit Hilfe von Vagrant und Ansible weitgehend zu automatisieren. Ein einfaches vagrant up oder vagrant provision reicht jetzt aus, um eine aktuelle Version von Zotero mit meiner Firefox-Runtime zu erstellen.

Nota bene: Ich verwende Pale Moon anstelle des offiziellen Firefox und muss daher die Namen des Installationsverzeichnisses und der ausführbaren Binärdateien anpassen.

Today I Learned: Normalisierung von Attributwerten

David Maus, 13.11.2018 · Permalink

Ein XML-Prozessor ist ein Programm, dass XML-Dokument gemäß der XML-Spezifikation verarbeitet. Wenn ein solcher Prozessor ein XML-Dokument einliest, dann werden die Werte in Attributen normalisiert, d.h. in eine standardisierte Form überführt. Die Regeln dieser Normalisierung sind in der XML-Spezifikation unter dem Punkt Attribute Value Normalization aufgeführt.

  1. Numerische Zeichenreferenzen werden durch das referenzierte Zeichen ersetzt.

    Der Prozessor ersetzt die Zeichenreferenz ä durch das referenzierte Zeichen 'ä' (U+00E4, LATIN SMALL LETTER A WITH DIAERESIS). Eine Anwendung, die mit dem so prozessierten XML-Dokument arbeitet "sieht" nie die Referenz, sondern immer nur das Zeichen.

  2. Entitätsreferenzen werden durch die referenzierte Entität ersetzt.

    Ist in der Dokumenttypdeklaration die Entität 'bspw.' als Zeichenfolge 'beispielsweise' deklariert, dann ersetzt der Prozessor jede im Attributwert vorkommende Referenz durch die entsprechende Zeichenfolge.

  3. Die Leerraumzeichen U+000D (CARRIAGE RETURN), U+000A (LINE FEED) und U+0009 (CHARACTER TABULATION) werden durch je ein Leerzeichen (U+0020, SPACE) ersetzt.

    Eine Ausnahme bildet die Abfolge von U+000D (CARRIAGE RETURN) und U+000A (LINE FEED), wenn sie aus einer deklarierten Entität stammt. Diese Abfolge von zwei Zeichen wird dann durch ein einzelnes Leerzeichen (U+0020, SPACE) ersetzt.

  4. Alle anderen Zeichen werden unverändert übernommen.

  5. Für Attribute deren Typ als Name, Nmtoken oder eine Liste eines dieser Typen definiert ist gelten weitere Regeln.

    In diesen Attributen können Leerzeichen allenfalls als Trennzeichen in einer Liste von Werten auftreten. Deshalb werden Leerzeichen am Anfang und am Ende des Attributwertes entfernt und aufeinanderfolgende Leerzeichen durch genau ein Leerzeichen ersetzt.

Notabene: Attribute, für die kein Datentyp definiert oder für die keine Datentypdefinition gelesen wurde, werden mit dem Datentyp CDATA prozessiert und unterliegen damit nicht dem fünften Normalisierungsschritt.

Praktisch bedeutet das, dass ich mich in der Arbeit mit XML nur auf die Normalisierungsschritte 1 bis 4 verlassen kann, da in den seltensten Fällen Datentypen definiert oder Datentypdefinitionen via Schemata gelesen werden. Glücklicherweise gibt es die XPath-Funktion normalize-space(), die den fünften und für die Arbeit mit Wertelisten notwendigen Normalisierungsschritt durchführt.

Sprich: tokenize(normalize-space(@ADMID), ' ') anstatt tokenize(@ADMID, ' ')

XProc Step by Step: Implementing a DOCX to TEI step

David Maus, 20.08.2018 · Permalink

Some of our projects use an XML first workflow where the principal researchers transcribe and annotate historical documents in Microsoft Word documents. While this solution is far from perfect, it allows humanists to capture valuable content with a tool they are comformtable with.

Such is the case in The Nuns' Network. The project aims at a digital edition of letters written by the Benedictine nuns of the Lüne monestery between 1460 and 1555. Being comprised of a couple of hundred letters, automating repetitive tasks is key. The project recently entered a stage were the publication workflow is stable enough to be eased and/or partially replaced with automated processes.

The first step of the current workflow is an initial transformation of a Word in a TEI document. The project uses the official transformation stylesheet provided by the Text Encoding Initiative and included in the <oXygen/> XML editor. It is executed manually: <oXygen/> provides an Ant-based scenario that unzips the Word document and applies the respective transformation.

In order to integrate this first step into an XProc pipeline I implemented this initial transformation as an XProc step. The step utilizes the XProc extension steps pxp:unzip and pxf:delete, and Calabash's cx:depend-on attribute. It unzips all XML documents to a temporary folder, runs the TEI transformation, and deletes the temporary folder.

The final TEI document is sent to the result port of the step.

docx2tei.xpl
<p:declare-step version="1.0" type="ndn:docx2tei"                xmlns:c="http://www.w3.org/ns/xproc-step"                xmlns:cx="http://xmlcalabash.com/ns/extensions"                xmlns:ndn="http://diglib.hab.de/edoc/ed000248/ns"                xmlns:pxp="http://exproc.org/proposed/steps"                xmlns:pxf="http://exproc.org/proposed/steps/file"                xmlns:p="http://www.w3.org/ns/xproc">  <p:output port="result" primary="true">    <p:pipe step="apply-transform" port="result"/>  </p:output>  <p:option name="inputFile" required="true"/>  <p:import href="http://xmlcalabash.com/extension/steps/library-1.0.xpl"/>  <p:variable name="outputTempDir" select="concat($inputFile, '.tmp')"/>  <p:variable name="outputTempDirUri" select="resolve-uri(encode-for-uri($outputTempDir))"/>  <pxp:unzip name="list-zip-directory">    <p:with-option name="href" select="$inputFile"/>  </pxp:unzip>  <p:for-each name="iterate-zip-directory">    <p:iteration-source select="/c:zipfile/c:file[ends-with(@name, '.xml') or ends-with(@name, '.rels')]">      <p:pipe step="list-zip-directory" port="result"/>    </p:iteration-source>    <p:variable name="outputTempName" select="/c:file/@name"/>    <pxp:unzip>      <p:with-option name="href" select="$inputFile"/>      <p:with-option name="file" select="$outputTempName"/>    </pxp:unzip>    <p:store method="xml">      <p:with-option name="href" select="concat($outputTempDirUri, '/', encode-for-uri($outputTempName))"/>    </p:store>  </p:for-each>  <p:load name="load-stylesheet">    <p:with-option name="href" select="resolve-uri('stylesheet/profiles/default/docx/from.xsl')"/>  </p:load>  <p:load name="load-source" cx:depends-on="iterate-zip-directory">    <p:with-option name="href" select="concat($outputTempDirUri, '/word/document.xml')"/>  </p:load>  <p:xslt name="apply-transform">    <p:with-param name="word-directory" select="$outputTempDirUri"/>    <p:input port="stylesheet">      <p:pipe step="load-stylesheet" port="result"/>    </p:input>    <p:input port="source">      <p:pipe step="load-source" port="result"/>    </p:input>  </p:xslt>  <pxf:delete recursive="true" fail-on-error="false" cx:depends-on="apply-transform">    <p:with-option name="href" select="$outputTempDir"/>  </pxf:delete></p:declare-step>

Unicode, UTF-8 und Kollation in MySQL

David Maus, 20.07.2018 · Permalink

Zeichenkodierung

Die Zeichenkodierung utf8 kann nur Unicode-Zeichen verarbeiten die sich in maximal 3 Byte UTF-8 kodieren lassen. Dadurch erschöpft sich der verwendbare Zeichenvorrat auf die Zeichen der Basic Multilingual Plane (BMP).

Die Zeichenkodierung utf8mb4 unterstützt auch Zeichen der höheren Unicode-Ebenen, allerdings um den Preis verkürzter Indexschlüssel.

Die Maximallänge dieser Schlüssel beträgt unabhängig vom Datentyp im Fall der InnoDB-Engine 767 Byte. Die Zeichenkodierung utf8 verwendet maximal 3 Byte pro Zeichen, so dass ein Indexschlüssel aus 767 ÷ 3 = 255 Zeichen bestehen kann; die Zeichenkodierung utf8mb4 verwendet maximal 4 Byte pro Zeichen, wodurch sich eine maximale Länge eines Indexschlüssel von 767 ÷ 4 = 191 Zeichen ergibt.

Für die MyISAM-Engine mit einer Schlüssellänge von 1000 Byte gelten die Einschränkungen analog.

Wenn ein Index über ein TEXT, VARCHAR oder CHAR-Feld benötigt wird, der mehr als 255 bzw. 191 Zeichen umfasst, dann ist der Einsatz eines Einweg-Prüfsummenalgorithmus mit geringer Kollisionswahrscheinlichkeit wie MD5 oder SHA1 als Option zu prüfen.

Kollation

Die Kollation legt fest, wie Zeichen miteinander verglichen werden. Sie gilt nicht nur für Suchanfragen, sondern auch für die Berechnung von Indexschlüsseln. Praktisch bedeutet das, dass bei Verwendung einer Kollation, die z.B. Umlaut und Grundbuchstaben gleich behandelt, für die Werte 'zahlen' und 'zählen' identische Indexschlüssel berechnet werden.

Gleichbehandlung von Umlaut und Grundbuchstabe
mysql> create database datenbank;mysql> use datenbank;mysql> create table tabelle ( text varchar(100) collate utf8_unicode_ci );mysql> insert into tabelle (text) values ('zahlen'), ('zählen');mysql> select count(*) from tabelle where text = 'zahlen';+----------+| count(*) |+----------+|        2 |+----------+1 row in set (0.01 sec)mysql> create unique index idx_text on tabelle (text);ERROR 1062 (23000): Duplicate entry 'zahlen' for key 'idx_text'

Gleiches gilt für die Groß- und Kleinschreibung.

...und von Groß- und Kleinschreibung
mysql> delete from tabelle;mysql> insert into tabelle (text) values ('zahlen'), ('Zahlen');mysql> create unique index idx_text on tabelle (text) ;ERROR 1062 (23000): Duplicate entry 'zahlen' for key 'idx_text'

Wenn ein UNIQUE Index verwendet werden soll aber Datenbankbenutzer auch komfortabel suchen können sollen, dann gibt es mindestens zwei Möglichkeiten: Konversion zur Abfragezeit oder Verwendung einer gesonderten Spalte.

Konversion zur Abfragezeit

Kollation zu Abfragezeit konvertieren
mysql> alter table tabelle change text text varchar(100) collate utf8_bin ;mysql> select count(*) from tabelle where 'zahlen' = text;+----------+| count(*) |+----------+|        1 |+----------+1 row in set (0.00 sec)mysql> select count(*) from tabelle where 'zahlen' = text collate utf8_unicode_ci;+----------+| count(*) |+----------+|        2 |+----------+1 row in set (0.00 sec)

Verwendung einer gesonderten Spalte

Eine Spalte text mit der Kollation utf8_bin wird für den UNIQUE-Index verwendet, eine Spalte text_suche mit der Kollation utf8_german2_ci für die Suche.

Andere Kollation in gesonderter Spalte
mysql> drop table tabelle;mysql> create table tabelle ( text varchar(100) collate utf8_bin; text_suche varchar(100) collate utf8_german2_ci );mysql> insert into tabelle (text, text_suche) values ('zahlen', 'zahlen'), ('zählen', 'zählen');mysql> select count(*) from tabelle where 'zahlen' = text;+----------+| count(*) |+----------+|        1 |+----------+1 row in set (0.00 sec)mysql> select count(*) from tabelle where 'zahlen' = text_suche;+----------+| count(*) |+----------+|        2 |+----------+1 row in set (0.00 sec)

Es ist abhängig vom Anwendungsbereich, welche der Möglichkeiten sinnvoll ist. Die Konversion zur Abfragezeit spart Speicherplatz auf Kosten der Rechenleistung, die Verwendung einer gesonderten Spalte spart Rechenleistung auf Kosten von Speicherplatz.

Umwandlung von Altdaten

Bei der Umwandlung von Altdaten sind in jedem Fall die verschiedenen Einstellungsmöglichkeiten der Zeichenkodierung zu beachten Schwartz, Zaitsev und Tkachenko 2012, S. 298ff. In der Regel werden die Zeichen "on the fly" bei einem Datenbankzugriff in die verbindungsspezifische Kodierung gewandelt. Dadurch ist nicht ohne weiteres möglich, festzustellen, wie ein Zeichen tatsächlich in der Datenbank gespeichert ist. Um das festzustellen kann der Text noch während der Abfrage in eine hexadezimale Darstellung gewandelt werden.

Feststellen, wie ein Text datenbankintern codiert ist
mysql> select hex(text),text from tabelle;+----------------+--------+| hex(text)      | text   |+----------------+--------+| 7A61686C656E   | zahlen || 7AC3A4686C656E | zählen |+----------------+--------+2 rows in set (0.00 sec)

Literatur

Schwartz, Baron, Peter Zaitsev, und Vadim Tkachenko. High performance MySQL: Optimization, backups, and replication. Beijing et al.: O’Reilly, 2012.

Today I Learned: ICC Farbprofil bei Imagekonversion mit ImageMagick erhalten

David Maus, 06.07.2018 · Permalink

Im Zuge einer geplanten und längst überfälligen Modernisierung der Wolfenbütteler Digitalen Bibliothek spiele ich mit dem Gedanken, zukünftig auch die bei der Digitalisierung verwendeten ICC Farbprofile mit in den veröffentlichten JPEGs anzubieten. Alle anderen Metadaten aus den TIFF-Dateien sollen aber wie gehabt entfernt werden.

Nach einigem Rumwerkeln bin ich auf folgende Lösung gekommen:

ICC Profile mit ImageMagick erhalten
magick convert 00001.tif 00001.iccmagick convert 00001.tif -strip -profile 00001.icc 00001.jpg

Im ersten Schritt schreibe ich das Farbprofil in eine temporäre Datei. Im zweiten entferne ich erst alle Metadaten und ergänze dann das extrahierte Profil.

<oXygen/>-Funktion "Transparenzwähler für Tags"

David Maus, 18.05.2018 · Permalink

Gerade entdeckt: Über die Konfiguration der Werkzeugleiste lässt sich in <oXygen/> unter dem Punkt "Quelle" das Werkzeug "Transparenzwähler für Tags" auswählen. Dieses Werkzeug ändert die Darstellung des XML-Quelltextes und erlaubt es, den Kontrast zwischen Markup und Inhalten zu ändern und damit aufgabenbezogen die Lesbarkeit von Markup bzw. Inhalt erhöhen, weil das jeweils andere durch Reduktion des Kontrasts in den Hintergrund rückt.

Ich kann mir vorstellen, dass das Ausblenden des Markups für die inhaltliche Korrektur tief getaggter Text hilfreich sein kann. Oder umgekehrt das Ausblenden des Inhalts für den Entwurf von XSL Transformationen.

Die unterschiedlichen Transparenzeinstellungen (<oXygen> 18, Windows)

HAB goes eduroam

David Maus, 23.11.2017 · Permalink

Update 2018-05-16: Nach dem Wechsel von OpenSSL auf GnuTLS musste ich die Option ca_path auskommentieren, um eine Verbindung zum eduroam-Netzwerk herzustellen.

Seit kurzem nimmt die Herzog August Bibliothek auch an eduroam teil. Damit können sich nicht nur Gastwissenschaftler mit ihren lokalen Anmeldedaten am hiesigen WLAN anmelden, sondern auch umgekehrt Mitarbeiter mit hiesigen Anmeldedaten am WLAN anderer Einrichtungen. Vorausgesetzt natürlich, dass die jeweilige Einrichtung sich auch an eduroam beteiligt.

Die Einrichtung auf dem Laptop war ein Kinderspiel. Einfach /etc/wpa_supplicant/wpa_supplicant.conf um folgenden Eintrag ergänzen:

Eintrag in wpa_supplicant.conf
network={  ssid="eduroam"  scan_ssid=1  key_mgmt=WPA-EAP  pairwise=CCMP  group=CCMP TKIP  eap=PEAP  identity="maus@hab.de"  domain_suffix_match="radius1.hab.de"#  Bei Verwendung von GnuTLS ca_path auskommentieren#  ca_path="/etc/ssl/certs"  password="XXXXXXXXXXXXXXXXXXXXXXXX"  anonymous_identity="anonymous@hab.de"  phase2="auth=MSCHAPV2"}

Die einzige Hürde beim Einrichten war, dass in der Nähe meines Büros noch ein alter AccessPoint steht, der als Identitätsnachweis nur ein selbstsigniertes Zertifikat anbietet.

HAB goes eduroam, Teil 2

David Maus, 02.05.2018 · Permalink

Auch die Einstellungen auf einem Android-Smartphone (Android 8.1) sind überschaubar und können ohne Zusatzanwendung vorgenommen werden. Im Verbindungsdialog folgende Einstellungen auswählen:

Einstellung eduroam auf Android 8
EAP-Methode PEAP
Phase 2-Authentifizierung MSCHAPV2
CA-Zertifikat Systemzertifikat verwenden
Domain radius1.hab.de
Identität maus@hab.de
Anonyme Identität anonymous@hab.de

A quick one: Use Metafacture to create a list of records with cataloging errors

David Maus, 24.04.2018 · Permalink

Some records in our library catalog contain an error that causes trouble when we distribute catalog data to national aggregators like the Zentrales Verzeichnis Digitalisierter Drucke (ZVDD), the central access point to printed works from the 15th century up to today, digitized in Germany. The catalogers made a typo and didn't separate the name of the publisher and the place of publication.

Metafacture is a really helpful suite of tools when working with a comparatively large set of records in a somewhat unwieldy format. The following Metamorph transformation runs over a dump of our catalog and outputs a list that contains the record's primary key (Pica production number, PPN), an indicator of the resource type, and the erroneous publication statement.

morph.xml
<metamorph xmlns="http://www.culturegraph.org/metamorph" version="1" entityMarker="$">  <rules>    <!-- Titel, bei denen Verlag und Ort nicht getrennt sind            -->    <!-- Bsp. http://uri.hab.de/instance/proxy/opac-de-23/391719076.xml -->    <!-- hier 033A $p[Strassburg: Prüss]                                -->    <combine name="" value="${ppn} - ${mat} - ${fehler-verlag}">      <data source="003@$0" name="ppn"/>      <data source="002@$0" name="mat">        <substring start="0" end="1"/>      </data>      <data source="033A$p" name="fehler-verlag">        <regexp match=".+: .+" format="${0}"/>        <normalize-utf8/>      </data>    </combine>  </rules></metamorph>

If the subfield p of the field 033A matches the specified regular expression, and both the subfield 0 of field 003@ and subfield 0 of field 002@ are present, combine these three fields to an unnamed output entity. Because 002@ and 003@ are always present, the combine acts as a filter and generates an output entity only if the erroneous 033A is detected.

I run this morph with a simple flux pipeline.

Flux pipeline
default src = "s:\\edoc\\ed000242\\2017\\20170429-title.pp";default dst = "stdout";src|open-file|as-records|decode-pica|morph(FLUX_DIR + morph)|encode-literals|write(dst);

Turns out that only 678 of appr. 1.2 million records or 0.06% are affected. This speaks volumes for the dedication of our staff and makes the problem managable.