page header

april 2010 Archieven

Fontnamen wijzigen in een OO document


Geplaatst door hjt op 2010-04-09 17:51:33 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Na mijn vorige blog over de verschillende versies van eenzelfde font, met alle lay-out ellende van dien, gaat dit blog-verhaal over het vervolg van de operatie.

We wilden meer controle over de fonts die in onze cursusdocumentatie voorkwamen. Helvetica (proprietary eigendom van Linotype) en Courier (proprietary eigendom van Monotype, oorspronkelijk voor IBM ontworpen), en Ariel en Verdana (beiden proprietary eigendom van Microsoft) wilden we toch maar uitbannen en vervangen door leden van de Nimbus-Sans familie, om meer controle over de weergave te krijgen. We wilden niet weer verrast worden door allerlei "automatische" font-substituties zoals die plaats kunnen vinden door het 'defoma'-mechanisme (Debian Font Manager): http://mintaka.sdsu.edu/GF/bibliog/latex/debian/defoma.html of zelfs door Adobe Acroread: http://www.creativepro.com/article/acrobat-tips-fonts-can-make-or-break-pdfs

Omdat .odg en .odp files XML-bestanden zijn kwamen we op het idee om een batchmatige fontnaam-zapper te bouwen. Dat werd een shellscript, plus een C-programma. Het script begint als volgt:

# Pak de .od[gp] file uit en onthoud in welke volgorde
# de onderdelen als aparte files eruit kwamen:
unzip "$1" > unzipsequence

ed -s unzipsequence << 'Here'
1,$g/^ *Archive:/d
1,$s/^ *[^:][^:]*: *//
1,$s/  *$//
w
q
Here

Dit script verwacht te starten in een lege directory, en $1 is de naam van de te bewerken .od[pg]-file (die dus in een andere directory moet staan). We unzippen die file, en vangen de outputboodschappen op. Die vertellen de volgorde waarin de onderdelen werden "ontzipt", en die volgorde willen we voor de zekerheid later weer reconstrueren.

In de daaropvolgende ed-sessie met Here-document trimmen we overbodig geachte boodschappen weg. Dat had ook wel met sed in een pipeline achter het unzip-commando gekund.

Het unzippen van een .od[pg] file levert een hele boom aan losse files en subdirectories op. Files met naam-extensie .xml vormen de kern van deze verzameling.

Die gaan we allemaal langs, en geven we aan ons C-programma dat in de scriptcode hieronder $ZAPFONTS is genoemd. Daarover straks meer:

# Selecteer uit de fileverzameling degenen waar fontnamen
# in kunnen staan. Geef die aan het 'zapfonts' programma.
# ODF files kunnen subdirs bevatten met z.g. embedded objects.
# Deze heten "Object 1" enz. met spatie in de filenaam.
# dus de dubbelquotes om "$name" zijn essentieel.
find . -type f -name '*.xml' | while read name; do
    $ZAPFONTS "$name" > tt
    mv tt "$name"
done

Nadat we alle fontnamen hebben vervangen, "zippen" we de verzameling losse files weer in elkaar. We gebruiken de output-boodschappen van de oorspronkelijke unzip om de files weer min of meer in hun oorspronkelijke volgorde te krijgen. Ik weet niet of dat nodig is, maar het leek me een nuttige (en niet zo moeilijk te realiseren) voorzorgsmaatregel.

# Voeg alle onderdeel-files weer bij elkaar:
cat unzipsequence | zip -X outfile.odp -@
mv  outfile.odp  "$1"
rm -rf *

De opnieuw ingepakte file schrijven we over de oorspronkelijk aangeleverde $1 filenaam heen, en we vegen de huidige directory weer net zo schoon als hij was aan het begin van de rit.

Een opmerking terzijde: als je in een shellscript het commando "rm -rf *" hebt staan, dan moet je wel heeeeel erg zorgvuldig alle cd-commandos in het script controleren. Want als een cd-commando mislukt dan ben je daarna met deze 'rm -rf *' goed de pineut. Gelukkig bevat het huidige script geen enkel cd-commando omdat het aanneemt in een lege directory te beginnen. Maar een 'if [ $(ls | wc -l) -ne 0 ]; then...' test aan het begin is toch verstandig. In andere scripts waarin ik zo'n 'rm -rf' moest verwerken heb ik na elk cd-commando een controle op de output van pwd toegevoegd.

Blijft over om een paar woorden te wijden aan het gebruikte ZAPFONTS C-programma. Ik zal u de source-code besparen maar liefhebbers kunnen op verzoek een kopietje krijgen.

Een xml-bestand bestaat uit tekstregels, dus in eerste instantie denk je in C aan een loopje in de trant van

while(fgets(......) != NULL) {
zap de fontnamen in deze regel
fputs(.......)
}

Dit is "Standard-I/O library voor beginners". Maar 'fgets' vereist dat je van tevoren een maximale regellengte moet inschatten. Toen ik wat XML-files bekeek om mijn schatting te onderbouwen bleek dat die soms waanzinnig lange regels bevatten: tot een paar duizend characters toe.

Uit mijn UNIX-jeugd, lang voordat het web bestond, herinner ik me een tijdschrift-artikel waarvoor iemand de meestgebruikte UNIX-commando's had getest op tekstfiles met lange regels: van 10000 characters per stuk. Bijna geen enkele utility overleefde (toen) de test. Bij deze XML-files moest ik ook oppassen.

Ik besloot mijn C-programma geen regels te laten lezen, maar porties van 1Kb. Dan loop je wel het gevaar dat een fontnaam precies over zo'n portiegrens heen ligt. Dus ik werk nu met een array van 2Kb, en ik begin met twee porties van 1Kb data uit de file te lezen, en achter elkaar in het array te plaatsen. Dan scan ik de eerste 1Kb-plus-een-paar-dozijn characters en zap de "foute" fontnamen. Dat stuk schrijf ik weer uit. Vervolgens schuif ik de tweede Kb in het array naar voren, en lees er een nieuwe 1Kb portie achteraan, enz.

Het feit dat een nieuwe fontnaam langer of korter kan zijn dan de oude naam die hij vervangt maakt het uitschrijven nog een beetje gecompliceerd, maar het is al met al best te doen. Ik heb dat in elk geval sneller zelf geprogrammeerd dan dat ik op het Internet moet zoeken naar een programma dat het kan, gevolgd door het beoordelen of dat wel solide genoeg is gemaakt.

Nadat een .odg of .odp bestand op deze manier gezapt is, is het verstandig om het even in OO te laden, en door te bladeren voor visuele inspectie. Wij vonden af en toe een plaats waar nog wat positioneringen op een pagina gecorrigeerd moesten worden. Maar in het algemeen is de operatie erg soepel verlopen.

Overigens: voor wie in typefonts is geïnteresseerd levert Wikipedia een schat aan informatie. Zoek maar eens naar een van de bekende font-namen.

OpenOffice font problemen


Geplaatst door hjt op 2010-04-01 14:09:36 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Bij ATComputing maken we het merendeel van onze cursusdocumentatie zelf. Dat is, denken wij, een fundament onder onze goede naam, maar ook een flinke last op onze schouders. We zitten daarbij natuurlijk niet te wachten op problemen met de gebruikte software. Toch hebben we al het nodige voor onze kiezen gehad.

Al vele jaren geleden zijn we voor onze presentaties (toen nog overhead-projectie) OpenOffice gaan gebruiken. Voorheen hadden we een commercieel pakket "Island Draw" onder Solaris gekocht. Maar de makers werden op een gegeven moment opgekocht door een concurrent, die het pakket daarna vakkundig de nek omdraaide. Wij bleven zitten met honderden bestanden in proprietary formaat, en executables die afhankelijk waren van een license key waarin een specifiek CPU-id was verwerkt. Gelukkig is de voormalige importeur van het pakket ons nog lang behulpzaam geweest met "naleveren" van benodigde keys. Maar op een gegeven moment deed ook zijn key-generator het niet meer. Van proprietary bestandsformaten hadden we onze buik inmiddels wel vol.

In ons bedrijf proberen we wat UNIX- en Linux versies betreft te voorkomen dat we eenkennig worden. Onze medewerkers gebruiken op hun laptops en hun thuis-computers allerlei verschillende distributies. Ook binnen het bedrijf is variëteit. Maar bij OpenOffice "draw" bestanden merkten we keer op keer dat bij Courier- en Nimbus fonts verschillen ontstonden: op de ene computer tekende je een alinea tekst met een randje eromheen of pijltje ernaartoe, en op een andere computer toonde dat bestand de tekst half buiten de rand, en de pijl ver naast zijn doel. Het probleem zat niet in zo'n bestand zelf: als je dat ongewijzigd terugverhuisde naar de oorspronkelijke computer stond alles weer netjes op z'n plaats. Nauwkeurig kijken maakte duidelijk dat ergens een font-metrics probleem moest zitten, met verschillend gedefinieerde letterhoogten voor hetzelfde font.

Ik ben nog steeds geen expert in de vraag waar een OpenOffice programma nu eigenlijk zijn fonts vandaan haalt. Wel weet ik dat dat een reuzendoolhof is. Hier speelt ook de X font server zijn rol mee. Een commando als 'xlsfonts' meldt vaak al een paar duizend fonts. Ons probleem werd niet alleen beïnvloed door de fontverzameling op de lokale machine, maar ook nog door de netwerk-wijd aangesproken fontserver.

Om toch ergens te beginnen zette ik 's avonds laat onze centrale font-server maar eens stil. Hier loert een soort Heisenberg-onzekerheid waarbij de manier van meten een grote invloed kan uitoefenen op wat je meet. Als OpenOffice bij afwezigheid van een fontserver een fall-back doet naar lokale fonts, dan kijk je misschien tegen een heel nieuwe situatie aan. Maar het probleem in de tekeningen bleef bestaan, dus moest binnen een lokale machine te traceren zijn. Mijn kansen stegen.

OpenOffice starten met een grafische muisklik is mooi, maar voor trouble-shooting niet handig. Via het configuratie-menu'tje voor de iconen kwam ik achter de naam van de executable file, maar dat was een shellscript. Langs een heel spoor van shellscripts en symlinks kwam ik uiteindelijk bij een "echte" 'soffice.bin' executable. Maar die is slechts een paar duizend bytes groot, dus daar zouden nog wel wat kindprocessen bij gaan horen. Nu maar eens kijken welke files een startende OpenOffice allemaal aanspreekt met mijn problematische tekening:

strace -f -e trace=desc -o mijntracefile \
          /usr/lib/openoffice/program/soffice.bin mijntekening.odg

Meteen na het zichtbaar worden van de tekening stopte ik OpenOffice weer. Mijn tracefile was "maar" 15000 regels groot geworden, hoewel 'trace=desc' het tracen beperkt tot file-descriptor gerelateerde system calls. Een tijdje rondbladeren gaf me de indruk dat de volgende selectie wel eens goed zou kunnen inzoomen: zoek naar de string 'font', maar ik hoef geen NOENT-regels (openen van een file mislukt) en geen configuratie-bestanden:

 grep -i font mijntracefile | grep -v -e NOENT -e conf

En jawel: nog maar een kort lijstje over, in deze trant:

7986  read(47, "lias our fonts to common familie"..., 8192) = 2332
7986  open("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", O_RDONLY) = 45
7986  open("/home/ikzelf/.openoffice.org/3/user/psprint/pspfontcache", O_RDONLY) = 47
7986  open("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", O_RDONLY) = 47
7986  open("/usr/share/fonts/type1/gsfonts/n021003l.pfb", O_RDONLY) = 58
7986  open("/usr/share/fonts/type1/gsfonts/n019003l.pfb", O_RDONLY) = 59
7986  open("/usr/share/fonts/type1/gsfonts/n019003l.afm", O_RDONLY) = 59
7986  open("/usr/share/fonts/type1/gsfonts/n019004l.pfb", O_RDONLY) = 62
7986  open("/usr/share/fonts/truetype/openoffice/opens___.ttf", O_RDONLY) = 62
7986  open("/usr/share/fonts/type1/gsfonts/n022004l.pfb", O_RDONLY) = 62
7986  open("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf", O_RDONLY) = 62

Interessante filenaam-extensies: .ttf voor TrueType font, .pfb voor PostScriptFont-binary, .afm voor AdobeFontMetrics

Vele jaren geleden heb ik eens als vingeroefening een compleet Adobe Type 1 font gebouwd, vanaf scratch. Ik had wel het allereenvoudigste gekozen: het KIX font. Dat is het alfabet van de barcodes waarmee de adres-sorteermachines van de NL posterijen werken. Je ziet het vaak op brieven onder de plaatsnaam staan. Geen ronde contouren, geen "hints" enz, alleen rechthoekjes in verschillende maten. Sindsdien weet ik nog de weg in .pfb en .pfa (PostScriptFont-ASCII) bestanden. .pfb-bestanden zijn lastig hanteerbaar: een mix van tekstregels en stukken binary. Commando's zoals vi, grep en less kunnen er niet mee omgaan, maar het kan wel zo:

strings  /usr/share/fonts/type1/gsfonts/n019003l.pfb | grep FontName
/FontName /NimbusSanL-Regu def

Bingo! Dit is een van de fonts waar het om gaat; ondanks de absurde filenaam hebben we 'm gevonden. We kijken nu wat verder in die file met 'strings':

%!PS-AdobeFont-1.0: NimbusSanL-Regu 1.06
%%Title: NimbusSanL-Regu
%Version: 1.06
%%CreationDate: Thu Aug  2 14:35:58 2007
%%Creator: frob
%Copyright: Copyright (URW)++,Copyright 1999 by (URW)++ Design &
%Copyright:  Development; Cyrillic glyphs added by Valek Filippov (C)
%Copyright:  2001-2005
% Generated by FontForge 20070723 (http://fontforge.sf.net/)
%%EndComments

Dan zoeken we op een van de andere hosts naar een file met dezelfde naam:

locate n019003l.pfb
.....
/usr/share/fonts/default/Type1/n019003l.pfb

Andere directory-padnaam, maar dat zal wel OK zijn. En we kijken met 'strings' ook in die file:

!PS-AdobeFont-1.0: NimbusSanL-Regu 1.06
%%Title: NimbusSanL-Regu
%%CreationDate: Tue Dec 31 16:51:01 2002
%%Creator: frob
%%DocumentSuppliedResources: font NimbusSanL-Regu
% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development; Cyri
% Generated by PfaEdit 1.0 (http://pfaedit.sf.net/)
%%EndComments

Wel wel... een duidelijk verschil. Toch ging het hier om twee actuele Linux-distributies, alleen van verschillend "merk"....

Om nu de rest van een lang verhaal kort te maken: we hebben op onze verschillende Linux'en geïnventariseerd welke versies van deze fonts voorkwamen. Dat bleek beperkt te blijven tot deze twee, hoewel de directorynaam bijna overal verschillend was. We hebben een directory met de 'CreationDate 2007' versies gepakt, en alle files daaruit botweg gekopieerd naar de Linux'en die de 'CreationDate 2002'-versies bevatten. Probleem opgelost..

Op onze checklist bij ingebruikname van "weer een nieuwe Linux"-distributie staat nu als vast punt erbij: font-files gelijktrekken.