Modularer Querbaukasten in PHP: Traits

Objektorientierte Entwicklung ist mittlerweile der Standard wenn es um Geschäftslogiken geht. Für verschiedene weitere Anwendungszwecke gibt es auch immer noch gute Funktionale Programmiersprachen. Jede Herangehensweise hat ihre Vor- und Nachteile.

Wie immer sind Programmierer komische Wesen und würden an einigen Stellen gerne das Beste aus beiden Welten haben, auch wenn diese sich eigentlich gegenseitig ausschließen.

Aktuell hatte ich mal wieder einen solchen Fall: Es gibt eine “fertige” PHP-Bibliothek zum Erzeugen von PDF-Dokumenten (diese eigenen sich in der Regel besser zum Ausdrucken als HTML-Seiten) – etabliert hat sich hier FPDF bzw. dessen Fork TCPDF. TCPDF ist die aktuellere und modernere Fassung, sie unterstützt unter anderem die Verwendung von UTF-8-Zeichensätzen. Daher musste ich mich auch “mal wieder” mit schon lange gepflegten und eigentlich abgeschlossenen PDF-Routinen beschäftigen: FPDF kann mit UTF8 nicht richtig umgehen, somit sehen die erzeugten PDFs aus wie Kraut und Rüben sobald Umlaute oder Sonderzeichen im Spiel sind.

Beim Umstellen gibt es ein paar Feinheiten, aber nichts was einen wirklich davon abhält die Umstellung halbwegs zügig durchzuführen. Die meisten Probleme erkennt man einmal und kann sie dann in den Folgedateien direkt korrigieren.

In diesem Zusammenhang habe ich mich mal wieder etwas geärgert: Viele unserer Dokumente enthalten Tabellen. Soweit so langweilig. Aus Mangel an Alternativen haben wir häufig den Code einfach von der einen eigenen Klasse in die nächste mit kopiert. Das Ergebnis ist altbekannt. Wenn man einen Fehler beseitigen will, muss man alle Kopien mit durcharbeiten. Effizient ist das nicht gerade.

Eine mögliche Option ist es eine eigene Klasse dazwischen zu setzen, die solche Funktionen realisiert. Aber was ist mit den Dokumenten wo man dann gar keine Tabelle braucht – da hat man Pech gehabt? Fazit das geht, aber richtig schön finde ich das auch nicht. Mehrfach-Vererbung kennt PHP nicht – aus gutem Grund (Diamond-Problem). Für meinen Fall hätte ich mir etwas gewünscht wie eine Art Plugin oder von mir aus auch eine Art “include” das entsprechende Funktionen einbindet wenn sie benötigt werden. Vergleichbar mit dem was ein Präprozessort für C erreichen kann: Macro-mäßig das einfügen was gerade gebraucht wird – bei compilierten Sprachen eine sehr feine Sache, bei Skripten leider kein gangbarer Weg.

Was braucht man eigentlich genau? bzw. was wünsche ich mir? – Das waren meine Fragen. Damit man eine Tabelle einfach einbauen kann braucht man Zugriff auf einige Klassenvariablen – man könnte sich ggf. behelfen, eine Referenz an eine Tabellenklasse zu übergeben und dort den notwendigen Code zu schreiben. Aber eine Tabelle ist ja auch nur wieder eine Sammlung von einfacheren PDF-Elementen – würde also auch wieder aus der TCPPDF-Klasse erben müssen, davon den Code in die andere Klasse zu übergeben mal ganz abgesehen. Eine Art “Nachbau” des Präprozessors wäre es wenn man hergehen könnte und an der benötigten Stelle in der Klasse einfach per include die Funktionen einbaut die man braucht. Das geht natürlich so nicht, auch weil es im Tabellencode ja durchaus sinnvoll ist, diesen strukturiert zu halten und keinen Spaghetti-Code zu erzeugen, den keiner mehr durchschaut. Auch die Nutzung von Klassenvariablen innerhalb der Tabelle wäre doch praktisch. Kurzum: ein simples Include kann das nicht leisten und sollte es auch nie.

Nun ist es ja aber bei weitem kein super exotisches Problem, mit dem wir hier konfrontiert sind: Modulbauweise mit Blackboxes die nur definierte Schnittstellen haben ist ja auch in anderen Bereichen üblich: Keiner entwickelt heute mehr für jedes Auto Standard-Komponenten vollständig neu – vielmehr werden Grundelemente mit einen neuen Aufsatz versehen oder neu miteinander kombiniert. PHP löst das Dilemma mit sogenannten Traits – ich vergleiche das gerne mit Matrix-Vererbung – um ein Gesamtpaket zu erhalten markiert man einfach die Zusatzfunktionen die das Ergebnis haben soll. Klingt kompliziert – ist es aber nicht.

Einen Trait programmiert man fast wie eine ganz normale Klasse – abgesehen, davon dass sie keinen eigenen Konstruktor enthält, gibt es alles was man sonst auch kennt: Funktionen, Zugriffsbeschränkungen (public,private,protected) und Klassenvariablen. Kurzum man kann alles was man immer wieder braucht damit sauber realisieren – in meinem Fall, habe ich alles was ich für eine Tabelle benötige im Trait verpackt. Man hat sogar Zugriff auf die Methoden und Variablen der Klasse in der man den Trait am Ende einsetzen will – hier ist die Unterstützung der Entwicklungsumgebung natürlich nicht so gut wie in einer vererbten Klasse, denn der Trait kann ja in jeder beliebigen Klasse verwendet werden – von daher etwas mehr Denkarbeit. Ich habe vieles direkt aus der bisherigen Realisierung übernehmen können, das macht es etwas leichter.

Was man noch beachten muss: Gleichnamige Funktionen in der übernehmenden Klasse “überdecken” bzw. überschreiben die Funktionen des Traits (vergleichbar mit dem was man aus der Vererbung kennt: Es wird immer die “nächstliegende” Funktion genommen, es sei denn man gibt den Namespace bzw. die Elternklasse mit an (parrent::) . Für mich hat es sich bewährt im Trait die Funktionen als solche entsprechend zu bennen – sie bekommen alle den Prefix “pdf_table_trait_” – die Wahrscheinlichkeit, dass man somit ungeplant etwas überschreibt ist vergleichsweise gering. Die Programmiersprache nimmt einem eben nicht alles ab und etwas Coding-Disziplin hat noch keinem Entwickler geschadet.

Insgesamt habe ich nun mit dem Trait das was ich gesucht habe: Einen Code-Block, den ich separat warten kann – wenn ich einen Fehler in der Tabellenerzeugung beseitige, dann muss ich das nur noch an einer Stelle tun, nicht mehr in jedem Dokument. Zudem habe ich es so realisiert, dass ich den Code sogar innerhalb einer Klasse mehrfach aufrufen kann um verschiedene Tabellen zu zeichnen (das hatte ich vorher mit etwas Verrenkungen unsauber gelöst). Man sollte natürlich jetzt nicht alles und jedes zwingend in einen Trait auslagern, nur weil es geht – mit Maß und Ziel das richitige Werkzeug an der richtigen Stelle ansetzen. Traits gehören dabei eindeutig in die Kiste mit der Aufschrift “Spezialwerkzeuge” – man braucht sie nicht all zu häufig, aber wenn man weiß dass es sie gibt und wie man sie richtig einsetzt, geht vieles sehr elegant zu lösen.

Mojibake – Buchstabensuppe

Na da hatte ich mir doch eine lange währende Aufgabe ausgesucht, die ich in der aktuellen Pflegephase meines Softwareprojekts endlich einmal angehen wollte. Aktuell läuft das Produkt soweit zufriedenstellend, nur an einigen kleiner Ecken knackt und knirscht es immer mal wieder. Unter anderem das leidige Thema “Sonderzeichen und deren Darstellung” – da nicht absolut zwingend für die Funktionsfähigkeit habe ich das längere Zeit nicht großartig beachtet.

Nun gut, was ist das eigentliche Problem und woher kommt es? Die Ursachen liegen in der Historie des PCs ver- oder besser begraben: Da Speicher und Datenverarbeitung teuer war, hat man so sparsam wie möglich gearbeitet. Man stellte dabei fest: Inklusive aller wichtigen Zeichen der englischen Sprache und allem was man als Steuerzeichen (Zeilenumbruch, Tabulator, etc.) benötigt, bekommt man in 7 Bit unter. Das ist schon mal etwas krumm, denn gängig ist bei allem was Rechner betrifft doch eigentlich etwas eine Potenz von 2. Die ersten Rechner die ich verwendet habe (mit einem Intel 8088 als Unterbau) konnten nur 8 Bit parallel verarbeiten – immer mehr als die 7. Schon wenig später gab es 16Bit als Standard und mit der großen Verbreitung des PCs in die Büros war der Standard 32 Bit erreicht. Heute findet der sich bereits größtenteils in Ablösung durch 64Bit-Systeme. Was war der Sinn des 8. Bits? Man konnte es je nach Gusto verwenden: Entweder bohrte man damit den Zeichenvorrat auf, oder man konnte es zur Fehlererkennung und Korrektur einsetzen. Insgesamt muss man sagen: Alles mit Hand und Fuß.

Nur leider waren Rechner damals noch nicht gängigerweise vernetzt und die Standard-Definitoren etwas kurzsichtig – erst einmal wurde nur an die eigene Heimat gedacht, dass es möglicherweise andere Sprachen gibt, die nicht Englisch sind und einige Zeichen mehr mitbringen – wer konnte das schon ahnen? So kam was kommen musste: Einschränkungen bei diversen Dingen wie dem Betriebssystem: Umlaute oder Sonderzeichen in Dateinamen waren einfach nicht vorgesehen oder führten zu unvorhergesehenen Ergebnissen. Für das Verfassen und Verarbeiten von Texten war das natürlich keine Option, wer in Deutschland einen Text schreiben wollte, der sollte das auch tun können. Daher erweiterte man den Zeichensatz auf das 8 Bit und schon hatte man jede Menge Möglichkeiten auch Sonderzeichen abzuspeichern. Wichtig war nur, das jeder die gleiche Zuordnung traf und nicht einer ein ü für ein ö verwendete was die binäre Darstellung betraf. Soweit so gut oder auch schlecht.

Nun gehen leider bei verschiedenen Betrachtungen immer wieder einige Dinge durcheinander, auch ich habe hier am Anfag so meine liebe Mühe gehabt alles richtig zu verorten, daher hier einmal die Kurzfassung, zu allem was notwendig ist bzw. was sich auf die Darstellung und Handhabung von Texten am Rechner auswirken kann.

Binärer-Code

Eine Abfolge von 0en und 1en die irgendetwas darstellen – was ist für den Rechner erstmal unerheblich, dafür bedarf es einer Zuordnungs oder Code-Tabelle oder eines Algorithmus, der den Binärcode in etwas umsetzt, das dem Benutzer eher vertraut ist (leider sind wir Menschen nicht sonderlich gut binär veranlagt).

Code-Tabelle/Zuordnung/Algorithmus

Je nachdem was für eine Datentyp abgebildet werden soll, gibt es verschiedene Methoden dies zu tun. Am einfachsten sind ganze, positive Zahlen. Diese lassen sich aus der Abfolge von 0en und 1en entsprechend “errechnen” – man muss nur noch wissen wo man anfangen muss (little endian vs. big endian). Für positive und negative Ganzzahlen hat sich das Zweierkomplement bewährt, für Fixkomma und Gleitkomma gibt es entsprechende Standards. Alles ein Thema für sich, denn wir wollen ja Texte bzw. Strings beleuchten. Hier hat sich eine Übersetzungstabelle als Mittel der Wahl erwiesen – sie ordnet jeder möglichen Abfolge von 0en und 1en eine menschenlesbare Bedeutung zu. So wird z.B aus der binären Folge 0110 1101  = 0x6D = O155 = “m”. Die Zuordnung an und für sich ist reine Willkür – man hat sich mal auf gewisse Standards geeinigt bzw. diese festgelegt, damit eben auf allen Rechner ein “m” ein “m” ist und nicht aus der “e-mail” durch Willkür eine “e-nail” wird. Der Urvater aller Standards dazu heißt ASCII (America Standard Code for Information Interchange) – wie der Name schon sagt ein nationales Standardformat. Auch bekannt ist diese Tabelle als Character-Set.

Windows-Codepages und Nornumg

Für verschiedene Regionen wurden verschiedene Verwendungen des verbliebenen 8. Bits (siehe oben) standardisiert – leider teilweise recht unterschiedlich, zum Teil nur an wenigen Stellen. Ziel war es, die in der jeweiligen Region verwendeten Sprachen möglichst gut abzubilden. Leider gab es da erst sehr spät eine Einigung auf einheitliche Standards, so dass zeitweise unterschiedliche Hersteller unterschiedliche Zuordnungen trafen. Ganz genauso wie wenn man Dateien ohne Angabe des verwendeten Zeichensatzes weitergab konnte es also passieren, dass der Zielrechner die Bit-Folge unterschiedlich interpretierte, weil bestimmte Bitfolgen eben etwas unterschiedliches bedeuteten. Dankenswerterweise hat man dabei die sichtbaren Zeichen (leider nicht so bei den Steuerzeichen) aus dem Urvater ASCII gleich belassen (das hatte sich ja bewährt, und für den internationalen Textaustausch reichte dieser kleinste gemeinsame Nenner auch aus). Gängig sind in der westlichen Welt die Standards der ISO 8859 in den Varianten 1-16, unter Windows die Codepage 1250 und unter DOS die Codepage 850.

Unicode

Mit der steigenden Internationalisierung und der immer stärker zunehmenden Vernetzung von Computern ist das mit den Nachschlagetabellen so eine Sache – man muss immer wieder etwas anderes berücksichtigen: Das Betriebsystem und die Region bzw. die Regionaleinstellungen des Anwenders – nur damit Text auch so ankommt wie er einegeben wurde. An einigen Stellen hat man sich beholfen oder die Probleme durch menschliche Intuition überbrückt – durch die Redundanz in der menschlichen Sprache kann man viele Worte ja auch lesen wenn Buchstaben vertauscht wurden oder nicht lesbar sind. Jeder Mensch der Lesen lernt kann das mehr oder weniger: Wenn aus einen Königreich dann plötzlich ein K$nigreich wird, dann wird der geneigte Leser das immer noch erkennen, auch wenn so ein Text etws mühsamer zu lesen ist. Ohnehin: wenn es international wird, dann ist die Sprache der Wahl in aller Regel doch Englisch und somit problemfrei. Aber wäre es nicht toll, wenn jemand eine Lösung hätte, damit ich auch chinesische Texte richtig angezeigt bekomme? Oder auch mal einen arabischen Text im Original anschauen? Das klingt weit hergeholt, aber diese Anwendungsfälle gibt es häufiger als man denkt. Die ursprüngliche Idee mit 32Bit und somit 4 Bytes pro codiertem Zeichen zu arbeiten (UTF-32) ist zwar einfach zu realisieren, aber ein wenig “over the top” ist das schon: Wer überträgt schon gerne 3 komplett genullte Bytes wenn die Info doch nur in einem steht? Außerdem ist der Standard nicht abwärtskompatibel, er bricht mit den alten Vorgaben von ASCII, was bei älterer Software oder inkompatibler Software Ärger machen kann. Daher gibt es verschiedene Methoden mit variabler Anzahl von Zeichen, sozusagen das Beste aus beiden Welten: UTF-16 oder UTF-8 sind hierbei die bekanntesten Möglichkeiten.

Schriftarten und Glyphen

Wir haben uns nun langsam von der Bits & Bytes-Ebene nach oben gearbeitet hin zum abstrakten Begriff eines “characters”. Es gibt aber noch eine Ebene obendran – die Schriftarten oder auch Glyphen. Das ist im Prinzip eine weitere Tabelle die festlegt wie ein abstraktes Zeichen auf dem Ausgabemedium dargestellt werden soll. Jeder der sich einmal durch den Schriftenkatalog einer gängigen Office-Sammlung gewühlt hat, weiß das ein A ein A bleibt, auch wenn es etwas unterschiedlich geformt ist. Das beste Beispiel ist noch immer die eigene Handschrift im Vergleich zu einem beliebigen, gedruckten Werk. So lange eine gewisse Basis-Form erhalten bleibt, erkennt jeder Leser darin auch weiterhin ein A. Es gibt natürlich auch wieder Spezielle Schriftarten die als Darstellungvon Buchstaben Symbole haben, die nichts mit dem eigentlichen Zeichen zu tun haben – das bekannteste dürfte die Schriftarte Wingding oder auch Webdings sein. Besonders unpraktisch fällt mir diese immer wieder in e-mails aus Outlook auf, wenn man diese als Text ließt. Dort wird der Smily 🙂 automatisch in ein J umgewandelt und mit der Schriftart Wingdings formatiert – sieht zwar im ersten Moment richtig aus, aber mancher hat sich schon gewundert wo das “J” denn nun herkommt. Ich merke es mir immer so: Ein Glyph macht aus einer Bit-Folge mittels einer Tabelle eine ganz bestimmte Grafik. Das ist zwar etwas vereinfacht, denn Schriftarten machen manchmal noch mehr, aber um den Überblick zu behalten reicht es allemal.

Collations

Was nochwas? Haben wir nicht alles endlich abgehandelt? Leider noch nicht ganz: Es gibt nämlich noch etwas was man mit Zeichenketten gerne macht: Aufreihen und Sortieren. Kling trivial, und es gibt doch Sortieralgorithmen wie Sand am Meer. Könnte man meinen, leider ist dem nicht ganz so (und das ist mit ein Grund weshalb man Sortieralgorithmen am besten an Zahlen erklärt …), denn auch wenn es im ersten Moment verlockend sein mag zu sagen: Man sortiert einfach nach der Größe der entsprechenden Ganzzahl der Bitfolge, das bringt leider nicht das gewünschte Ergebnis, denn schon bei ASCII haben wir ja große und kleine Buchstaben, ordnet man nun nach der errechneten Wertigkeit, so würde das folgende Sortierreihenfolge ergeben A,B,C…..,X,YZ,a,b,c – für die alphabetische Auflistung eines Materialkatalogs nicht das gewünschte Ergebnis – auch im Telefonbuch sucht man die Leute mit “von” im Vornamen ja nicht unter “v” sondern unter “V” und dort irgendwo nach “Voldemort”…. und nun wirds ganz interessant: Wo ordne ich denn die Sonderzeichen wie Ä,Ö,Ü ein … sind das separate Zeichen am Ende? Oder soll ich sie behandeln wie “Ae”,”Oe” und “Ue”? Das ganze nun noch auf die internationale Ebene gehoben und es wird ganz spannend: Je nach Land gibt es gleiche Zeichen, aber die werden unterschiedlich einsortiert, aber der Rechner soll es dennoch richtig machen. Das kann nur bedingt funktionieren, alles weitere regelt man über die Collation, die kann man zur Not auch bei der Sortierung mit angeben, und dem Sortierprogramm somit die Regeln vorgeben.

So jetzt habe ich einmal die Grundlagen zusammengefasst, die Auswirkungen und Mittel die man braucht um das alles umzusetzen behandle ich in einem separaten Artikel.

 

 

Typo3 und ImageMagick – was zu beachten ist

Für einen guten Zweck arbeite ich auch als Hosting-Anbieter für die Interessensgemeinschaft Herzogenried. Die Verbindung zum Quatiermanagement entstand schon vor einigen Jahren, damals ging es primär darum überhaupt eine Präsenz im Netz aufzubauen. Der verantwortliche Mitarbeiter war mir schon von daher sympathisch, dass er vieles bis ins Detail verstehen wollte und auch im Netz selbstständig agieren wollte. Er hat sich mit enormen Eifer und einiger Unterstützung damals die Grundlagen von HTML selbst beigebracht.

Zwischenzeitlich ist der Bedarf gewachsen, es wurde umgestellt auf Joomla! als Content-Management-System (CMS) – für mich auch keine große Sache, jedoch habe ich mich nicht in der Tiefe mit dem System auseinander setzen können wie ich es gerne gewollt hätte. Aber die Mannschaft hat es auch so recht gut hinbekommen – einige Kleinigkeiten habe ich noch unterstützend beitragen können.

Nunmehr stand wieder ein Neuanfang auf dem Programm – von Zeit zu Zeit sollte jede Homepage mal einem Review und ggf. auch einer Modernisierung unterzogen werden. Wie sich zeigte war Joomla! nicht mehr die absolute Präferenz, zumal sich nun auch Personen mit deutlich besseren Kenntnissen in Sachen Layout und Gestaltung gefunden hatten. Die neue Homepage soll unter Typo3 entstehen. Mir ist das System letztlich egal – jeder soll das System verwenden mit dem er am Besten arbeiten kann. Nicht jedes Werkzeug ist automatisch für jeden Zweck geeignet nur weil viele es für diesen einsetzen.

Für mich als Systemadmin ändert sich durch den Wechsel des CMS eigentlich nichts – die Anforderungen sind recht human: Apache als Webserver (es gingen wohl auch einige andere), MySQL bzw. MariaDB als Datenbank und PHP als Skriptsprache die das ganze zusammen hält. Soweit nichts außergewöhnliches.

Schon interessanter werden da diverse nette Features die Typo3 so mitbringt, wie etwa das automatische Skalieren von Bildern oder auch die Erzeugung von PDFs. Alles Funktionen die gut ankommen wenn man sie richtig einsetzt. Für die ganze Bildbearbeitung kommt ein mir nicht unbekanntes Tool zum Einsatz ImageMagick – diese in C geschriebene Toolbox ist das Schweizer Taschenmesser was automatisierte Bildbearbeitung betrifft – die Bedienung der Tools auf der Kommandozeile scheint anfänglich etwas kryptisch, aber gerade für Stapelaufgaben ist sie hervorragend geeignet. Die Libary dazu habe ich schon länger auf meinem Server installiert – für meine Webprojekte verwende ich direkt die PHP-Schnittstelle, die auf den Namen Imagick hört. Diese ermöglicht unter anderem die Berechnung von Thumbnails on demand, und ist dabei dank C-Implementierung um ein vielfaches schneller als die GDLib, die sonst in PHP für die Bearbeitung von Bildern verwendet wird. Ich selbst habe Imagick schon für die von mir betreute Website der THW-Jugend in Mannheim verwendet – irgendwann war es mir einfach lästig geworden jedes Mal für eine Bildergalerie die Thumbnails von Hand zu berechnen – mit ein klein wenig Datenbank-Background habe ich das dann aus der Welt geschafft. Klingt zwar im ersten Moment etwas schräg ausgerechnet Bilder als Binary large Object in eine Datenbank zu packen, aber die Optionen sind einfach zu schön. Mal ganz davon abgesehen, dass man sich nicht mehr um irgendwelche Dateirechte in shared hosting Umgebungen Gedanken machen muss – auch etwas wie Schlagworte oder Gruppierungen sind in einer relationalen Datenbank einfach wesentlich leichter möglich, als wenn alles im Dateisystem liegt.

Typo3 geht einen etwas anderen, universelleren Weg und ruft das Tool “convert” aus dem Imagick-Paket auf – das macht es mächtiger aber in meinen Augen auch umständlicher zu bedienen. Jedenfalls ist die Installation auch kein Hexenwerk. Paketverwaltung auf – installieren, ja, Installation fertig. Soweit mal die Vorbereitung.

Die Rückmeldung lässt nicht lange auf sich warten: ImageMagick funktioniert generell, aber wir haben ein Problem mit der Version … der Kunde wünscht in diesem Fall eine ältere Version – was mir als Betreiber natürlich etwas widerstrebt, gibt es doch zahlreiche Bugfixes auf die ich selbst angewiesen bin und diverse kleinere Lücken im Code, die geschlossen wurden.

Nach etlichem Hin und Her ist das Problem zumindest einmal eingegrenzt: Wann immer man mit Typo3 versucht eine Adobe Illustrator-Datei in ein JPEG zu überführen, knirscht es bezüglich der verwendeten Transparenz. Der Hintergrund wird nicht weiß sondern schwarz dargestellt, was gerade beim Weiterverwenden in PDFs nicht gut aussieht.

In diversen Foren ist das Problem auch beschrieben und es gibt diverse Abhilfe Vorschläge – unter anderem ein Downgrade, was für mich ja wie geschrieben eigentlich nicht in Frage kommt. Also geht die Suche nach der Ursache weiter – ich bin ja sogar Willens mich ggf. mit dem Code von Image Magick auseinander zu setzen – Bildbearbeitungsalgorithmen habe ich zwar mal gelernt, aber so schwer kann es in dem Fall ja nicht werden.

Über mehrere Zwischenstufen kristallisiert sich für mich langsam die Struktur von ImageMagick heraus – mit Nichten ist das eine allwissende Bibliothek mit angeschlossenem Werkzeugkasten, wie ich lange Zeit gedacht hatte. Vielmehr lagert ImageMagick diverse Aufgaben an dezidierte Programme aus – was ja auch sinnvoll ist, man braucht das Rad ja nicht zweimal zu erfinden und nirgendwo ist die Wiederverwendung von Programmen und Code so einfach wie in Software und gerade im OpenSource-Bereich.

Ich lerne Adobe Illustrator Dateien sind nur verkappte PDFs mit einigen Erweiterungen – zum Lesen eignet sich das vielseitige Ghostscript, das ursprünglich mal als PostScript-Interpreter gestartet ist. Man muss wissen, dass PDF anfänglich auch nur eine etwas aufgebohrte Version von PostScript war. PostScript war lange Zeit die Sprache um Drucker sauber und effizient anzusteuern – ein guter Netzwerkdrucker versteht sich zur Freude von Linux-Anwendern und Mac-Nutzern auch heute noch auf PostScript. Die Logik zur Aufbereitung von Dokumenten und Druckvorbereitung lag damals schon im Drucker. Erst mit GDI und PCL gab es die ersten “Thin”-Printer – also Drucker ohne große Eigenintelligenz die vom Rechner “ferngesteuert” wurden. PDF war der Schritt vom Papier in die digitale Welt – also eine Art unabhängiges Dokumentenformat, das sich aber auch auf Referenzen verstand.

Halten wir also fest Typo3 ruft ImageMagick bzw. dessen Tool convert mit einigen Parametern auf, das wiederum übergibt die Sache zumindest teilweise an Ghostscript. Was genau wohin übergeben wird, steht in der

delegates.xml

– wo genau die sich findet hängt ein wenig von der Distribution ab – unter Ubuntu findet man sie unter [code]/etc/ImageMagick/delegates.xml[/code].

Einer der Tipps aus den Foren lautet den vollständigen Pfad für ghostscript gs  anzugeben, das betrifft aber im Wesentlichen die Windows-Welt – schaden kann es ja mal nichts, aber leider bleibt mir der Erfolg versagt. Das Problem besteht weiterhin – die Hintergründe werden schwarz anstelle transparent (also weiß) dem auf Papier (es sei denn man druckt gerne auf schwarz, wobei dann die meisten Drucker mit ihren Farben an die Grenzen des Möglichen kommen – weiß als Tinte oder Toner habe ich noch bei den wenigsten Druckern gesehen).

Nächster Schritt in der Fehlersuche: Handarbeit: Also einmal ein Bild von Hand auf der Kommandozeile mit convert umwandeln – die Befehlszeile dazu lautet anhand von Typo3:

convert' +profile '*' -geometry 170x170! -colorspace RGB -quality 70  'typo3logotype.ai'[0] 'install_read_ai.jpg'

Was mir auffällt: Das Ausgabe-Format ist JPEG – wie ich weiß und mich auch nochmals informiere unterstützt JPEG keine Transparenz (dafür gibt es andere praktische Formate wie PNG oder früher auf GIF) – indem ich mir ein PNG ausgeben lasse, kann ich das Problem wieder ein Stück herunter brechen: Das Einlesen funktioniert wohl sauber, denn das ausgegebene Bild enthält korrekt weiterhin die Transparenz – ergo muss irgendwas bei der Umwandlung in JPEG schiefgehen.

Die Lösung des Problems ist recht simpel:

-background white -flatten

in die Angabe eingebaut und schon kommt das passende Ergebnis bei heraus – es geht unter anderem natürlich auch mit anderen Farben als Hintergrund.

Nun ist eine Lösung an sich ja schon mal was tolles, aber ich will ja wissen warum es früher mal so funktioniert hat und jetzt nicht mehr. Wie schon vermutet hat sich in der neuen Version von ImageMagick etwas bei JPEG schreiben verändert. Die Funktion zum “plattklopfen” bzw. dem Zusammenführen von Ebenen aus einen konstruierten Bild wird jetzt nicht mehr implizit aufgerufen. Bei vorherigen Versionen war dies der Fall wenn Transparenz vorlag und ein Hintergrund gegeben wurde. So eine simple und auch gut nachvollziehbare Änderung (jede Funktion sollte das machen was drauf steht, das richtig, aber auch bitte nicht noch mehr), hat mich hier eine ganze Weile auf Trab gehalten.

Wenn man den Aufruf in der Typo3-Konfiguration anpasst, dann klappt auch die Umwandlung ohne Probleme. Nur schade, dass diese so simple Lösung (die noch dazu auch mit älteren Versionen von ImageMagick richtige Ergebnisse liefert) den Weg noch nicht in die aktuelle Fassung von Typo3 gefunden hat. Wollen wir hoffen, das dies bald der Fall ist.

MySQL und der Null-Pointer

Da denkt man mal wieder an nichts Böses und prompt holt einen das 2. Gebot der C-Programmierung ein – obwohl man nicht in C programmiert, sondern eigentlich nur in einer Datenbank unterwegs ist.

Die 10 Gebote finden sich hier:

http://www.geekhideout.com/c-ten-commandments.shtml

Und die Aussage “Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.”  ist mir dabei mal wieder wie Schuppen von den Augen gefallen.

Was war passiert? Ich habe eine Datenbank-Tabelle, die mir angibt welche Subunternehmen an einer Arbeit mit beteiligt werden: Pro Arbeit kann es mehrere Subunternehmer geben, und jeder Subunternehmer kann an beliebig vielen Arbeiten mitwirken. Soweit so gut, ein klassischer Fall einer n:m Beziehung (wie man das im ER-Modell bezeichnen würde, UML ist da etwas genauer und würde in diesem Fall von einer 0..n:0..m Beziehung sprechen). Nun habe ich feststellen müssen: Selbst wenn das offiziell nur ein Subunternehmen ist, dann kann das immer noch recht groß ausfallen – die reine Angabe einer Firma als Referenz reicht also nicht unbedingt aus. Soweit ja kein Problem: Es gibt ja auch noch eine Tabelle aller am Projekt beteiligten Mitarbeiter – und ggf. ist einer davon als Ansprechpartner definiert. Leider nur ggf. und nicht zwingend – bei kleineren Firmen gibt es eine solche Zuweisung leider nicht.

Nun ja, was macht der geneigte Datenbank-Programmierer, genau, er macht das was er mal gelernt hat: Wenn es keine Referenz gibt, dann tragen wir den speziellen Wert “NULL” ein. Selbst die referentielle Integrität kommt damit nicht aus dem Tritt – vielmehr ist es ein ganz klares Zeichen: Hier muss gar nicht erst nach einem passenden Partner in der anderen Tabelle gesucht werden – es gibt ihn schlichtweg nicht. Alles nicht richtig spannend und für mich bis dato kalter Kaffee.

Spannend wurde es im Zusammenhang mit der Modifikation der Datensätze: Um zu verhindern das Doppelte Einträge vorhanden sind, habe ich einen Unique-Key über die drei Spalten “Arbeit”,”Firma”,”Ansprechpartner” angelegt. Zudem enthält die Tabelle noch ein paar zusätzliche Information, die aber nichts zu Sache tun. Was liegt also näher als mit einem “Insert ignore”, “Upsert” bzw. dem praktischen MySQL-Konstrukt “Insert into …. on duplicate key update ….”  zu arbeiten? Das ist im Prinzip wie geschaffen für den beschriebenen Fall: Gibt es schon einen entsprechenden Eintrag in den  Schlüsselspalten, dann muss man nur noch die Satellitendaten entsprechend dem Bedarf aktualisieren (das lässt z.B. gut Luft für eine Überwachung durch wen und wann die letzte Änderung durchgeführt wurde).

Doch was ist das: Beim Testen der Funktion tauchen nach dem Speichern der Modifikation plötzlich Einträge doppelt und dreifach auf – also gerade nicht das was man erwartet hätte.

Erster Schritt in der Fehlersuche: Irgendwas im Code wurmig? – Nein, da stimmt alles und es gibt auch keine enstsprechenden Extra-Inserts – die Statements sehen so aus wie man sich das gewünscht hat. Nächster Schritt: Statements “manuell” ausführen und … aha! – man kann Einträge bei denen der Ansprechpartner auf “NULL” steht beliebig oft einfügen ohne dass die Schlüsselmechanismen ansprechen…. erster Verdacht: MySQL-Bug! – aber weit gefehlt: “It’s not a bug, it’s a feature” zumindest in den Augen der MySQL-Entwickler … aber Abhilfe gibt es auch: einfach einen “primary” anstelle eines “unique” keys nehmen…. gesagt getan: Aber dann hagelts erst richtig: Denn in einem “Primary”-Key dürfen keine “NULL”-Werte auftauchen – was macht MySQL intern: Man behilft sich, und verändert sie Spalte – sie darf jetzt nicht “NULL” enthalten und der Default-Wert ist “0” – und das trotz eines Fremdschlüssels? – Und genau da beißt sich die Schlange dann in den Hintern … denn die bestehenden Daten sind komischerweise “valide” – keine Schlüsselverletzung. Aber Einfügen geht nicht mehr: Will man “NULL” einfügen sperrt sich die Tabellendefinition, will man “0” verwenden, gibt es von Seiten der referentiellen Integrität auf die Finger … So ein Mist!

Lösungen gibt es bisher dafür keine 😐 zumindest nicht auf Datenbank-Ebene. Ich habe jetzt einen Hack-Around gemacht: Man lösche alle Einträge zur Arbeit und lege sie danach alle wieder an und schon kann man mit dem Unique-Key leben, man hat praktisch die Prüfung etwas weiter nach oben verlagert (wo sie in meinen Augen nicht hingehört).

Aber wie schon da Commandement von oben sagt: Null-Pointer sind ganz böse Dinge – und wenn man nicht aufpasst findet man sich in der Hölle wieder 😉