– ASCII-Terminalfont in SVG mit Scala –

asciifont

Mal wieder was technisches, für zwischendurch.

Themen: SVG, Inkscape, Ansi/ascii, Font, Bitmap, Scala

Werfen wir einen trüben Blick zurück in die 90er, als ich meinen ersten PC beschaffte, einen 386er mit sensationellen 8MB RAM, also rund einem 1000tel des heutigen, einer 40MB Platte, ähnlicher Faktor, 16 Mhz, auch rund die Größenordnung. Anfangs-OS war MS-DOS irgendwas u. Windows 2.0, erst ein, zwei Jahre später lernte ich über Modem Rico kennen, meinen langjährigen Linuxguru, der inzwischen vom reinen Glauben abgefallen ist ins Lager der Leute mit den weißen Kabeln.

Er konnte mich zwar früh für Linux interessieren, allein meine ursteure Kiste (wg. dem RAM – üblich waren eher 1-2MB, aber ich hatte ein Oracle für den PC, welches dringend 8MB benötigte mit einer Extensioncard, groß wie ein heutiges Tablet) erkannte im BIOS keine Festplatten > 80 MB. Ein netter Händler in der Kastanienallee, in der ich damals wohnte, hatte aber eine Spezialkarte, die das BIOS austrickste, und über die ich bis zu 4 128-MB Festplatten anschließen konnte, was ich im Laufe der Zeit dann auch tat. Nur erkannte Linux die nicht, weswegen ich Linux erst auf meinem nächsten PC installieren konnte.

Derart in der DOS-Welt gefangen kam mir dann aber bald ein Paket Borland C/C++ über den Weg gelaufen, es muss von einem LKW gefallen sein, denn die Anschaffung wäre wieder eine kostspielige Sache gewesen, und damit beschäftigte ich mich ausführlich und autodidaktisch, und eine meiner Spielereien war damals, dass ich die Zeichen des Ascii-Zeichensatzes auf der Konsole mit dem Borland-Grafikbefehl getpixel auslas, und den Font von 8×16 Pixeln als Array aus 0en und 1en auf Platte schrieb. Die weitere Verwendung muss hier nicht weiter erörtert werden – es hielt sich auch sehr in Grenzen.




{ // 83 S
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0},
{1,1,0,0,0,1,1,0},
{1,1,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0},
{0,0,1,1,1,0,0,0},
{0,0,0,0,1,1,0,0},
{0,0,0,0,0,1,1,0},
{1,1,0,0,0,1,1,0},
{1,1,0,0,0,1,1,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
},



Schon länger schleppte ich die Idee mit mir rum diesen Font irgendwie für Inkscape-Vektorgrafiken bzw. das SVG-Format nutzbar zu machen. (Haben Sie diese Code-Tags bemerkt? Einfach ignorieren!)

Letzte Woche schaute ich nun einerseits ein paar Inkscape/SVG-Tutorials an, darunter solche die sich der XML-Manipulation widmeten, und las 2-3 Artikel u. Videos

zum Tod von Zapf Dingbats, nein, Moment, zum Tode eines Herrn Herrmann Zapf, auf den verschiedene, populäre Fonts zurückgehen, darunter Optima, Zapfino (klingt wie ein brasilianischer Fußballer) und Zapf Dingbats. Ein rotes A einer seinere Schriften (Zapfino?) ist auch das Logo einer internationalen Atheistenorganisation:

Als Logo eine fragwürdige Wahl insofern man es schwer abmalen kann, verglichen mit dem Anarcho-A (A) (allgemeine Bekanntheit vorausgesetzt).

Man kann sagen, die Lage war überdeterminiert nun endlich zur Tat zu schreiten.

Störend war, dass ich an mehreren Stellen zugleich anfangen musste zu bohren – das Handling von XML mit Scala war mir, ohne je richtig vertraut gewesen zu sein, weitgehend entfremdet und das SVG-Format weitgehend unbekannt.

Einen ziemlichen Dämpfer erhielt die Angelegenheit bei der Betrachtung, was Inkscape daraus macht, wenn man einen Kreis mit Durchmesser 10 auf einem Rechteck in der linken, oberen Ecke platziert. Inkscape hat einen extra Button um sich das produzierte XML anzusehen, und das sieht so aus:

  <g
 id="g21429">
 <rect
 y="-0.15551876"
 x="0.125"
 height="75.625"
 width="45.75"
 id="rect21425"
 style="fill:#0000ff;fill-opacity:1;stroke:none" />
 <path
 transform="translate(-1.625,-2.875)"
 d="m 17.875,10.969481 a 8.125,8.125 0 1 1 -16.25,0 8.125,8.125 0 1 1 16.25,0 z"
 sodipodi:ry="8.125"
 sodipodi:rx="8.125"
 sodipodi:cy="10.969481"
 sodipodi:cx="9.75"
 id="path21427"
 style="fill:#ffff00;fill-opacity:1;stroke:none"
 sodipodi:type="arc" />
 </g>

Okay - was haben wir da? Ein Rechteck mit x,y und Weite, Höhe, y="-0.15551876" also rund 17 Nachkommastellen. Stroke=none? Das heißt Umrandung=keine, kann man das dann nicht einfach weglassen?  

Dann kommt path, wo ich circle erwartet hätte, und es geht gleich los mit transform="translate(-4.6042403,-3.0545715)" - was ist das nun wieder? Und sodipodi? Ja, Du mich auch! Okay, soviel hatte ich vom Tutorial noch im Kopf: Anfangs hieß das Projekt 'Sodipodi', nicht 'Inkscape', und daher findet sich das noch im Code. Cx, cy sind Center-x und -y - das überrascht nicht. Rx, ry dagegen ein wenig, aber ein Kreis ist nur der Spezialfall einer Ellipse, für Inkscape, bei der x-Radius und y-Radius identisch sind. Oben steht noch ein d-Block, d="m ..." mit m für move (die Stiftspitze) und zwar relativ zur letzten Position, Groß-M wäre absolut, wenn ich das richtig im Kopf habe - das ist irre! Fügt man irgendwo eine neue Figur so ein muss man alle vorherigen Stiftbewegungen nachvollziehen, um zu wissen, wo der Stift zuletzt war, und fügt man etwas ein oder löscht etwas weg, dann stimmen alle nachfolgenden, relativen Bezüge nicht mehr! 

Wofür a steht weiß ich nicht mehr, wohl Stift absenken oder sowas. Diese ganze Zahlenkolonne ist jedenfalls höchst fehleranfällig. Das z am Ende bedeutet aber ‚zurück‘ zum Pfadbeginn also, d.h. die Figur wird geschlossen, soviel weiß ich noch.

Kurzer Exkurs zu Kreisen, Ellipsen und Bögen:

kreise

Oben ein Kreis, darunter eine Ellipse mit rx != ry. Man kann allerdings den Kreis erst quetschen und dann um 90° drehen, so dass man einer Täuschung unterliegt, welcher der Radien nun kleiner ist. Die Rotation der Figur kommt also als Option hinzu. Dann kann man den Kreis an 3 Anfassern bei Inkscape manipulieren. Oben und links den Radius, rechts den Anfasser um den Kreis aufzureißen, wodurch man ein Tortensegment macht (Mitte) oder ein Kreissegment (rechts). Außerdem – hier nicht gezeigt, kann man eine Kontur dazu zeichnen oder nur die Kontur und die Kontur kann gepunktet oder gestrichelt sein, die Füllung ein Muster oder ein Farbverlauf, die Dicker der Umrandung, die Transparenz, pipapo – sodipodi!

Und unten ist das Malheur passiert. Man hat die Knotenwerkzeuge eingeschaltet (links) und kann die Knoten reindrücken oder rausziehen (Mitte) oder die tangentiale Richtung der Hülle am Knoten manipulieren (rechts).

Alles tolle Sachen, aber wir haben vor einen Asciizeichensatz mit nicht ganz 256 Zeichen zu erstellen, jedes Zeichen 8 Pixel breit und 16 Pixel hoch und dargestellt von je einem Kreis. Ob da ein Kreis in 4 Zeilen oder einer oder 96 Zeilen dargestellt wird macht einen kleinen Unterschied. 8×16 ist ja 8²2 oder 642 = 128. Das mal 200 (weil einige Codes nicht druckbare Zeichen darstellen, etwa Tab und Newline) sind 25.600 Kreise maximum, da die Flächen schwach gefüllt sind vielleicht 8-10.000, aber immerhin. Bei 10 Zeilen pro Kreis wären das 100.000 Zeilen Code.

Dann spielte ich aber rum mit dem „Speichern unter“-Dialog, der neben „Inkscape SVG“, was ich normalerweise nutze, noch „normales SVG“ kennt und „optimiertes SVG“ (hört, hört!). Und siehe da, das verständliche aber ausufernde Sodipodizeuch kann man weglassen.

Wer die Links oben zu SVG-Tutorial brav besucht hat wird schon unruhig auf dem Sitz hin- und herrutschen. Ja – es geht auch einfacher.

Ich speicherte eine Datei, öffnete sie mit dem Editor und fügte aus dem Tutorial ein einfaches und plausibles:

 <circle cx="50" cy="50" r="40" stroke="none" fill="red" />

ein, und sagte in Inkscape Datei/Zurücksetzen, dann wird sie neu vom Datenträger gelesen. Und siehe da – beim Lesen versteht SVG diese einfache Sprache.

Jetzt hieß es nur noch das oben vorgestellte Bitmapformat aus C mit Scala, meiner Haussprache (neben Bash und Sed), in XML zu verwandeln.

package bitmap
/* 
 List (256x Bitmap of List ( 16 Zeilen of String (8 x 0 oder 1)))
*/
object Bitmap {
val bitmap: List[List[String]] = List (
List ( // 0
"10000000",
"01000000",
"00111000",
"00100000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",
"00000000"
),
List ( // 1
"00000000",
"00000000",
"00000000",
"00000000",
"00000000",

Mit sed änderte ich oben gezeigten C-Code so um, dass es eine Scala List-of-List-of-Strings wurde. Zeichen 0 und 1 werden (wie alles <32) nicht wirklich genutzt und bestehen nur aus 0en, außer beim ersten Zeichen aus Testzwecken.

import bitmap._

object ToSvg extends App {

 def rect (id: Int, rx: Int, ry: Int, bmap: List[String]) = {

 def circle (x: Int, y:Int) = {
 val sx = "" + x
 val sy = "" + y 
 <circle cx={sx} cy={sy} r="5" style="fill:#ffff00;stroke:none" />
 }
 
 def rect (gid: String, pid:String, px:String, py:String) = {
 <g id={gid}>
 <rect id={pid} height="160" width="80" y={py} x={px} fill="#0000ff"/>
 {
 for (col <- (0 to 7);
 row <- (0 to 15);
 x=5+10*col;
 y=5+10*row;
 if (bmap(row)(col) == '1') 
 ) yield circle (x+rx, y+ry)
 }
 </g>
 }
 rect ("g" + (1000 + id), "rect" + (1000 + id), "" + rx, "" + ry)
 }
 
 override 
 def main (args: Array[String]) {
 val bm = Bitmap.bitmap
 val rectlist = (32 to 127).map (i => rect (i, (i % 16)*100, (i/16)*200, bm(i)))
 println (rectlist.mkString ("\n"))
 }
}

Einerseits ist die Möglichkeit XML direkt in Scalacode zu integrieren verlockend – andererseits kann es auch extrem irritierend werden, zwei Sprachen ineinander gemixt zu haben.

Das Ergebnis war eine hübsche Armee von knapp 100 Zeichen aus blauen Rechtecken mit gelben Kreisen darauf, die Buchstaben ergaben, jeweils gruppiert. Beim Ausschneiden wird aber wohl immer ein Pfad mit diesem kryptischen Format aus den Gebilden, und so vollführte ich die Fleißarbeit, und vereinigte die Kreise jedes Zeichen und zog dann diese Vereinigung von der blauen Fläche ab, von Hand. So kann man eine farbige Fläche unter das Zeichen legen, so dass diese aus einer Matrix durchscheint.

Oben im Startbild sollte es zu sehen sein.

Der Font ist nicht als Font nutzbar, also dass man ihn auswählt, und dann lostippen kann. Man muss sich zeichenweise die Buchstaben hinkopieren, diese grob nebeneinander anordnen (Zeile 1), und kann sie dann auf einer Linie in der Höhe ausrichten (Z.2). Wenn man sie leicht überlappend zusammenschiebt kann man sie dann vom Ausrichtungstool soweit auseinanderrücken lassen, dass sie sich gerade nicht mehr überschneiden.

Dann kann man sie zu einem Pfad vereinigen und einen oder mehrere Hintergründe dahinterlegen. (Z. 3-7).

Das SVG-Beispiel zeigt nochmal 3 Zeichen in Groß, gestaucht horizontal u. vertikal sowie mit der Perspektiven-Erweiterung verzerrt. Darunter an einem Pfad ausgerichtet (Band, wiederholt). Letztlich war noch Platz für ein Smilie u. ein Copyright-Symbol, Recht- und Ordnung müssen ja sein! :)

Bei OpenClipart.org finden Interessierte die beiden Versionen des Fonts und eine Version der erweiterten Ascii-Tabelle mit äöüÄÖÜß und manch anderem Westeuropäischen Gedöns.

Fragen sind willkommen – den kompletten Code für bitmap.Bitmap gibt es nur auf Wunsch – könnte man ja auf github … – mhm. Wer weiß wo die Sekretärin das Passwort versteckt hat…

Update: Zur bequemen Eingabe von Texten, die man in eine Grafik packen will, gibt es jetzt ein Script.

Advertisements

Ein Gedanke zu „ASCII-Terminalfont in SVG mit Scala

  1. Enno

    Ich kämpfe auch gerade mit XML. Genauer mit RSS-feeds. Germanpod101.com. Nicht nur, dass das unnötig aufgebläht ist. Die Elemente sind auch nicht konsistent benannt. Da kann man gut sehen, dass der Praktikant alle 6 Monate gewechselt hat. Ein Albtraum zum Archivieren.

    Antwort

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s