momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Conversia unui site HTML în document PDF

HTML | LaTex | MetaPost | pandoc
2019 apr

A First Guide to PostScript (Peter Weingartner, 2006) este un tutorial de PS (recomandat ca atare în diverse locuri) prezentat ca site HTML; pagina de bază conţine o listă de link-uri (un "Contents") către celelalte pagini şi fiecare dintre acestea are un link spre pagina de bază şi unul spre pagina care îi urmează în listă; fiecare pagină este de fapt un fişier HTML de sine stătător, conform standardului "XHTML 1.0 Transitional".

În [1] pusesem problema de a transforma un astfel de site în "document PDF" (ceea ce este mai pretenţios decât pentru o pagină HTML, caz în care se ştie că putem utiliza facilităţile browser-ului); folosind pandoc (şi încă, diverse programe utilitare), dusesem cumva lucrurile până în punctul în care nu mai aveam de făcut decât să transform vreo 400 de link-uri către fişierele HTML (sau părţi ale acestora), în "referinţe interne" documentului rezultat - moment în care bineînţeles că m-am oprit: mi-a scăpat ceva din vedere - ceva care probabil că este aşa de simplu încât s-ar merita să reluăm investigaţia chiar de la capăt (şi chiar - ca de obicei când reiau - foarte minuţios).

1. Fişierele site-ului; simplificări conjuncturale

Constituim directorul 4apr/ şi descărcăm (folosind wget) arhiva oferită prin ultimul link din "Contents", de pe site-ul menţionat:

vb@Home:~$ mkdir 4apr;  cd 4apr
vb@Home:~/4apr$ wget http://www.tailrecursive.org/postscript/postscript.tar.gz

Dezarhivăm (folosind tar) şi inspectăm (cu ls) fişierele din directorul rezultat:

vb@Home:~/4apr$ tar xvzf postscript.tar.gz
vb@Home:~/4apr$ ls postscript
books.html       escapes.html   language.html     printable.html
clipping.html    examples       mailme.html       programming.html
color.html       examples.html  notes.txt         resources.html
contacting.html  faq.html       nup.html          text.html
draft.html       galley.html    operators.html    transforms.html
drawing.html     graphics.html  postprocess.html  verbotten.html
eps.html         image.html     postscript.css    what-is-it.html
errors.html      index.html     postscript.html

Se cuvine să inspectăm şi fişierele din subdirectorul examples/:

vb@Home:~/4apr$ ls postscript/examples
box1.html      color.html       line.ps         scale.ps         transform.html
box1.ps        color.ps         newsprint.html  shadewidth.html  transform.ps
box2.html      fill-box.html    newsprint.ps    shadewidth.ps    translate.html
box2.ps        fill-box.ps      rotate.html     smile.html       translate.ps
cliptext.html  hello-text.html  rotate.ps       smile.ps
cliptext.ps    hello-text.ps    scale.html      text1.ps

În examples/ avem câte un fişier .html şi un fişier .ps cu un acelaşi nume; constatăm (deschizând într-un editor de text, gedit) că fişierul HTML este unul "de sine stătător" (ca şi toate celelalte) şi conţine două link-uri la pagina examples.html, iar între acestea figurează un link la fişierul .ps omonim şi un element <pre> în care este redat codul PS respectiv. Pare clar că putem ignora fişierele examples/*.html, incluzând doar codurile-sursă .ps (sau invers, dar vom decide mai târziu).

În directorul postscript/ nu avem fişiere imagine, dar se cuvine să verificăm dacă nu cumva în fişierele .html existente apar elemente <img> care impun descărcarea de imagini de pe alte site-uri. Următorul script Perl extract_src.pl (similar celui din [1]) extrage din elementele <img> existente în fişierele HTML indicate, valorile atributului src (deci adresele de la care browser-ul va descărca imaginile respective):

#!/usr/bin/perl -n
# adresele imaginilor din fişierele HTML indicate
while ( /img.*src="(.+\.png)"/g ) {
    	print "$ARGV: $1\n";  # nume fişier şi adresa imaginii incluse
}

Obs. Pentru simplitate, am vizat numai formatul de imagine PNG.

Invocăm acest script (după ce-l facem "executabil", prin chmod +x) pentru fişierele HTML din postscript/:

vb@Home:~/4apr$ ./extract_src.pl postscript/*.html
postscript/index.html: http://creativecommons.org/images/public/somerights20.png
postscript/postscript.html: http://creativecommons.org/images/public/somerights20.png

Avem deci două fişiere, care conţin o aceeaşi imagine (şi avem şi adresa de la care trebuie descărcată aceasta); le deschidem în gedit şi le comparăm: constatăm repede că există o singură diferenţă importantă, anume în index.html avem <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">, iar în postscript.html avem <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ...>.
Prin urmare, putem foarte bine să ştergem unul dintre cele două fişiere; alegem să eliminăm 'index.html', fiindcă "HTML 3.2" este totuşi o specificaţie arhaică (şi probabil, acest fişier a fost uitat pe site dintr-o primă ediţie a acestuia); în plus, am verificat în prealabil (folosind grep) că în celelalte fişiere nu avem referiri la "index.html".

Descărcăm (cu wget) imaginea respectivă, plasând-o în directorul postscript/ şi modificăm corespunzător atributul src din <img>, în fişierul postscript.html.

2. Clarificări; ordinea fişierelor, "Contents" şi secţiuni

Ar fi acum momentul să clarificăm ce vrem să facem; vrem să obţinem un document PDF, care să "reunească" toate paginile site-ului. Putem să facem aceasta manual (şi oare n-ar fi aşa, cel mai simplu?!); preferăm totuşi să folosim pandoc - instrument foarte capabil (şi în fond, simplu de folosit), destinat conversiei dintr-un limbaj de marcare în altul. Mai ales dacă procedăm manual, dar şi dacă implicăm pandoc - ordinea în care tratăm fişierele iniţiale contează, iar unele dintre acestea vor trebui "tratate" separat (de exemplu, cele din directorul examples/, cum am observat mai sus). Preferăm să generăm o formă intermediară (fişier LaTeX) pe care să putem interveni şi manual, înainte de a obţine forma finală PDF; şi de fapt, n-ar fi deloc rău dacă mai întâi am genera un fişier HTML5 pe care să-l transmitem apoi lui pandoc pentru a obţine fişierul LaTeX (intervenţia în HTML5 poate fi mai convenabilă decât în LaTeX şi poate ne ajută cumva, faptul că HTML5 este mai "capabil" (sau nu?!) decât formatul "XHTML 1.0" al fişierelor iniţiale).

În postscript.html, sub titlul '<h2>Contents</h2>' avem o listă de 15 link-uri către pagini HTML din directorul postscript/ (plus un link pentru a descărca arhiva site-ului, link pe care putem de-acum să-l ştergem); postscript.html este "pagina de bază" a site-ului şi prin click pe unul sau altul dintre link-urile din "Contents", se accesează una sau alta dintre paginile tutorialului (care conţin şi link-uri înapoi către pagina de bază). În principiu, tutorialul trebuie parcurs într-o anumită ordine a paginilor, corespunzând în mod firesc ordinii în care apar cele 15 link-uri din "Contents" (iar această ordine devine obligatorie, în cazul când am tipări întregul tutorial).

Prin următorul script extract_sections.pl extragem "Contents" (mai precis, valorile atributelor href din elementele <a> şi titlurile aferente) din postscript.html:

#!/usr/bin/perl -n
# fişierele de prelucrat, în ordinea secţiunilor
while ( /<li><a\s+href="(.*?)">(.*?)<\/a>\s*<\/li>/ig ) {
    	print "$1\t -- $2\n";  # fişier -- titlul secţiunii
}
vb@Home:~/4apr$ ./extract_sections.pl postscript/postscript.html
what-is-it.html	 -- What <i>is</i> PostScript?
graphics.html	 -- Graphics Concepts in PostScript
language.html	 -- Language Concepts in PostScript
programming.html	 -- Programming in PostScript
drawing.html	 -- Drawing and Filling Shapes
text.html	 -- Putting Text on the Page
color.html	 -- Adding Color
transforms.html	 -- Transformations
clipping.html	 -- Clipping for Effect
image.html	 -- Raster Graphics
eps.html	 -- Encapsulated PostScript
postprocess.html	 -- Funky Stuff
examples.html	 -- Index of Examples
operators.html#opindex	 -- Index of Operators
errors.html	 -- Listing of Errors

Cele două coloane – separate prin TAB ('\t') şi " -- " – reprezintă respectiv numele fişierelor în ordinea în care va trebui să le furnizăm lui pandoc şi numele secţiunilor corespunzătoare ale tutorialului; vom putea separa prima coloană (pentru pandoc) folosind cut -f -1.
Putem verifica printr-un script similar celui de mai sus (while(/<(h.?)>(.+?)<\/h.?>/g){print "$ARGV: $1 - $2\n";}) că toate fişierele .html din postscript/ au titlurile marcate fie cu <h1>, fie cu <h2>; elementele H1 vor corespunde în LaTeX cu \sections, iar H2 cu \subsections.
Fişierul postprocess.html (pentru secţiunea "Funky Stuff") conţine trei link-uri la fişiere HTML "secundare" (neaflate între cele 15 de mai sus), anume galley.html, nup.html şi draft.html - dar aceste fişiere au titlurile marcate (ca şi în cazul fişierelor "principale") cu H1; vom transmite şi aceste trei fişiere lui pandoc (anume, imediat după "Funky Stuff"), dar ca să apară ca subsecţiuni ale lui "Funky Stuff" (cum este firesc) va trebui în prealabil să înlocuim H1 cu H2, pentru titlurile iniţiale ale acestora şi mai mult, va trebui să înlocuim elementele H2 existente (şi există unul sau două, în fiecare) cu H3 (pentru a păstra şi în LaTeX, subordonarea secţiunilor).

3. Simplificări

Fie postscript/sections.txt fişierul care conţine prima coloană (separată cum am indicat mai sus, din rezultatul furnizat de ./extract_sections.pl), completată cu cele trei fişiere tocmai menţionate. Înainte de a invoca pandoc (transmiţându-i lista sections.txt, cum am arătat în [1]), să ne gândim dacă ar mai fi de făcut vreo simplificare.
…dar întâi avem de observat o completare firească: trebuie să înscriem în lista sections.txt, chiar pe primul loc, postscript.html (care corespunde paginii "de bază").

Vrând să "reunim" fişierele indicate de sections.txt, nu are sens să păstrăm link-urile existente către "Main/Next/Previous Page"; aceste link-uri apar în câte o diviziune <div class="navbar"> şi pentru ştergerea acestora din toate fişierele HTML din postscript/, cea mai simplă şi rapidă soluţie o avem (sau nu?!) folosind sed:

vb@Home:~/4apr/postscript$ sed -i '/<div class=\"navbar\">/ , /<\/div>/ d'  *.html

Specificatorul final d asigură ştergerea textului cuprins (inclusiv) între cele două "adrese" separate prin virgulă, în fiecare dintre fişierele primite ca intrare. Desigur, lucrurile trebuie verificate (ştim că ştergerea de date este o operaţie periculoasă); în cazul de faţă, am avut surpriza să constat că am pierdut întregul conţinut al fişierului postscript.html! L-am reconstituit din arhivă (şi n-am uitat să modific <img> şi să elimin ultimul link din "Contents") şi am observat că are la început şi la sfârşit două linii <div class="navbar">&nbsp;</div> care nu au nici un sens (diviziunile respective nu conţin decât un spaţiu; să le înfiinţezi numai pentru a le orna cu un 'background' şi un 'padding' cum se prevede pentru div.navbar în postscript.css este chiar ridicol); desigur, comanda sed de mai sus ar fi trebuit să şteargă numai aceste linii, dar cum-necum (nu mai stau să-mi explic;… sau nu?!), a şters tot conţinutul…

Obs. Ba chiar, trebuie să "stau să-mi explic"! M-am obişnuit să folosesc "one-liners" (cu perl, sed sau awk) pentru modificări pe fişiere text; dar am observat nu o dată, că procedând aşa de "simplu şi rapid" în cazul textelor marcate cu HTML, poţi avea surprize neplăcute (precum cea evidenţiată mai sus, a cărei lămurire merită rezervată unui articol independent).

4. Comasarea fişierelor HTML într-un fişier HTML5

Ignorând momentan alte fişiere, să le "împachetăm" într-un fişier HTML5 pe cele 19 vizate de sections.txt; invocăm pandoc cum am arătat în [1] (dar acum pentru conversie din HTML în HTML5), cu deosebirea că folosim şi opţiunea --css pentru a indica fişierul de stiluri CSS al site-ului (şi renunţăm la variabile 'colorlinks', etc.):

vb@Home:~/4apr/postscript$ file_list=(` cat sections.txt `)  &&  pandoc -s \
>     -f html -t html5  ${file_list[@]}  --css=postscript.css \
>     -M title="A First Guide to PostScript" \
>     -M author="Peter Weingartner" -M date="24 February, 2006" \
>     -o fgps-1.html
pandoc: operators.html#opindex: openFile: does not exist (No such file or directory)

Până să obţinem rezultatul fgps-1.html, iată că avem de rezolvat o eroare, care era cumva de aşteptat: pandoc cere ca argument lista fişierelor pe care să le prelucreze; de obicei, aceasta înseamnă (sau nu?!) o listă de "nume de fişiere", ori în cazul nostru "operators.html#opindex" nu denumeşte vreun fişier… Corectăm (eliminând "#opindex") în sections.txt şi reluăm comanda, de data aceasta cu succes.

Obs. Cum altfel, să rezolvăm această eroare?! Căutăm pe Internet - dar afli doar că şi alţii au păţit-o; verificăm versiunea de pandoc - poate că actualizarea la ultima versiune disponibilă a avut în vedere eroarea de acest tip… Aici am preferat "rezolvarea" banală, dar sigură.
De menţionat şi calea de rezolvare (cea mai bună! sau nu?!) avută în vedere de regulă, de software-ul "open-source": studiază codul sursă şi modifică-l dacă te pricepi, pentru a-l adapta cazului tău; pentru pandoc, ar trebui să stăpâneşti acceptabil (şi) limbajul Haskell.

Am obţinut cum ne propusesem, un fişier HTML5 fgps-1.html, "reunind" în ordinea din sections.txt, 19 dintre fişierele HTML iniţiale; dar se pare că nu avem vreun avantaj: să copiem fgps-1.html (eventual şi postscript.css, dar nu şi alte fişiere din directorul postscript/) într-un alt director şi să-l deschidem de aici în browser - vom constata că orice link am accesa cu mouse-ul, obţinem informarea "File not found"… Aceasta pentru motivul că s-au păstrat link-urile din paginile HTML iniţiale, spre fişiere HTML din directorul părinte al fişierului "de bază". Cu alte cuvinte, am ajuns în acelaşi impas ca în [1]: ar fi nevoie să modificăm cumva (totuşi… nu "manual") link-urile respective, încât ele să refere subsecţiuni ale fişierului fgps-1.html şi nicidecum, alte fişiere HTML.

5. Iluminare: cine formulează "Contents" (TOC)?!

Dacă am ajuns pe căi cumva diferite, la un acelaşi impas - înseamnă că ne-a scăpat ceva mai de la începutul lucrului; rezolvarea principială a întregii chestiuni trebuie să plece mai de deasupra lucrurilor concrete care ne sunt prezentate.

Site-ul de la care am plecat este "concentrat" (parţial) în fişierul fgps-1.html şi văzându-l aşa, îl putem încadra în categoria generală "articol" (sau "articol electronic", dacă avem în vedere posibilitatea interacţiunii cu utilizatorul); de regulă, conţinutul propriu-zis al unui articol este repartizat pe secţiuni şi subsecţiuni şi el trebuie acompaniat cu diverse informaţii despre articol (necesare de exemplu, în vederea citării articolului respectiv în alte lucrări): numele autorului, fireşte, dar eventual şi un "rezumat", un "Cuprins", etc.

Deja de mult timp, lumea ştiinţifică foloseşte LaTeX pentru scrierea articolelor. În LaTeX adăugarea unor informaţii auxiliare ("cuprins", "lista figurilor" sau tabelelor, "glosar", etc.) este automatizată; de exemplu, macroul \tableofcontents selectează declaraţiile de secţiuni şi subsecţiuni din textul articolului şi generează un TOC ("Table Of Contents"). Poate că autorul face modificări (adaugă secţiuni, schimbă titluri ale acestora, etc.) pe parcursul lucrului la conţinutul propriu-zis; prin recompilare se va obţine (automat) TOC-ul corect al articolului (îmi amintesc că am mai vizat generarea TOC-ului, acum vreo 10 ani: Adăugarea cuprinsului, folosind XML::Twig).

pandoc prevede opţiunea --toc, pentru a asocia (automat) documentului final un TOC; să transformăm folosind această opţiune, fişierul fgps-1.html obţinut deja mai sus:

vb@Home:~/4apr/postscript$ pandoc -s -t html5 --toc fgps-1.html -o fgps.html
vb@Home:~/4apr/postscript$ cp fgps.html ../;  cd ../
vb@Home:~/4apr$ firefox fgps.html 

Am obţinut fişierul fgps.html, pe care l-am copiat în părintele directorului care îl conţine şi l-am deschis în Firefox.

Singura deosebire faţă de fgps-1.html este că fgps.html conţine la început (imediat după titlul "articolului" şi numele autorului) TOC-ul generat automat de către pandoc (pe baza elementelor <h1> şi <h2> din fişierul iniţial fgps-1.html):

<nav id="TOC">
<ul>
<li><a href="#about-this-document"><span id="about">About this Document</span></a></li>
<li><a href="#contents">Contents</a></li>
... ... ...
<li><a href="#errors-you-might-encounter">Errors You Might Encounter</a></li>
</ul>
</nav>

Click pe oricare link din diviziunea marcată cu id="TOC" conduce la zona din document al cărei ID este înscris (cu prefixul "#") în atributul href al link-ului respectiv. Acum putem elimina din fgps.html, "Contents"-ul moştenit din fişierul iniţial postscript.html: îl înlocuim folosind "Copy&Paste", cu TOC-ul generat în capul documentului de către pandoc; desigur, putem reduce TOC la un singur nivel, încât să apară exact la fel ca vechiul "Contents" (şi nu uităm, să eliminăm itemul <li><a href="#contents">Contents</a></li>).

Să observăm că este vorba aici de interacţiunea cu ecranul ("conduce la zona" înseamnă că zona respectivă este vizualizată imediat în fereastra curentă a browser-ului); pe hârtie (folosind meniul "Print" al browser-ului) bineînţeles că nu are sens "click", iar TOC-ul existent devine oarecum inutil - spre deosebire de cazul documentului PDF, când în TOC se precizează şi numărul paginii corespunzătoare secţiunii (încât TOC este "funcţional" nu numai pe ecran, dar şi pe hârtie).

6. Corectarea link-urilor interne unor secţiuni

În TOC-ul generat de pandoc găsim itemul "PostScript Operators", conţinând (într-un element <ul>) o listă de elemente <a> care vizează operatorii PS consideraţi (iar ultimul link din această listă vizează "Index of Operators"):

<li><a href="#postscript-operators">PostScript Operators</a><ul>
<li><a href="#operator-add"><span id="add">Operator: add</span></a></li>
...
<li><a href="#index-of-operators"><span id="opindex">Index of Operators</span></a></li>
</ul>

Deschizând fgps.html în browser şi făcând click pe primul item din lista de operatori redată mai sus, obţinem în fereastra respectivă:

Deducem că zona din document corespunzătoare unui operator XXX este accesată din TOC prin /fgps.html#operator-XXX. Însă purtând mouse-ul peste link-urile existente în zona obţinută astfel (de exemplu pentru fereastra redată mai sus, pe "stackunderflow", sau pe "sub"), vedem (în "bara de stare" a ferestrei) că acestea vizează /operators.html#XXX (sau /errors.html#stackunderflow, etc.).

Prin urmare, pentru a elimina legăturile cu fişierul "operators.html" (să lăsăm deoparte alte fişiere HTML vizate şi ele în anumite link-uri, precum "errors.html"), trebuie să înlocuim "operators.html/#XXX" cu "fgps.html#operator-XXX" (şi de fapt, numai cu "#operator-XXX", fiindcă "fgps.html" este implicit), în atributele href respective:

vb@Home:~/4apr/postscript$ perl -pi -e \
> 's/href="operators.html#(.+)"/href="#operator-\1"/g' fgps.html

Ca urmare a acestor înlocuiri, cele peste 250 de link-uri existente în fgps.html care vizează cei 45 de operatori PS descrişi în secţiunea "Postscript Operators", funcţionează acum corect (nu mai implică fişierul "străin" operators.html, ca înainte de înlocuire). Dar pentru "Index of Operators" (către care există în document o singură trimitere, anume din interiorul sub-secţiunii "About this Document"), trebuie acum să schimbăm href din "#operator-opindex" cum tocmai a devenit, în "#index-of-operators".

Obs. De fapt, iarăşi am avut o "surpriză neplăcută": după execuţia comenzii de mai sus am constatat că încă au rămas câteva referiri la "operators.html"; dacă pe o linie existau mai multe astfel de referiri, comanda respectivă a înlocuit-o doar pe prima dintre ele! Am deduce că modificatorul "g" asigură operarea pe toate liniile textului, dar nu pe toate apariţiile dintr-o aceeaşi linie. Rezolvarea ad-hoc constă în repetarea comenzii, de încă două ori.

Există aproape 130 de trimiteri la erorile catalogate în secţiunea "Errors You Might Encounter" şi pentru ca ele să funcţioneze corect (să nu mai implice fişierul "străin" errors.html) putem reedita linia de comandă de mai sus:

vb@Home:~/4apr/postscript$ perl -pi -e \
> 's/href="errors.html#(.+)"/href="#\1"/g' fgps.html

În "About this Document" (şi nicăieri altundeva) apare şi 'a list of various <a href="errors.html" id="3">errors</a>' şi trebuie să corectăm acum, punând href="#errors-you-might-encounter".

Acum, referinţele la operatori şi la erori funcţionează corect, din oricare punct al articolului în format HTML5 fgps.html.

7. Inserarea exemplelor de programe PS

În TOC-ul generat de pandoc avem şi itemul "Index of Examples" (pentru pagina iniţială examples.html, inclusă şi aceasta în lista sections.txt pe baza căreia am obţinut fgps.html); acesta vizează o listă de 13 link-uri (la fişierele HTML din examples/, listate la §1):

<h1 id="index-of-examples">Index of Examples</h1>
<ul>
<li><a href="examples/newsprint.html">Clipping Text to a Box</a></li>
... ... ...
<li><a href="examples/smile.html">Drawing a Raster Image</a></li>
</ul>

Să procedăm ca în §4 (implicând însă direct scriptul extract_sections.pl din §2), pentru a "reuni" fişierele examples/*.html într-un fragment HTML5 "appendix_examples.html" (invocăm pandoc fără opţiunile -s şi --css; altfel, am obţine un fişier HTML5 complet):

vb@Home:~/4apr/postscript$  \
>    file_list=(` .././extract_sections.pl examples.html | cut -f -1 `) \
>      &&  pandoc -f html -t html5 ${file_list[@]} \
>      -o appendix_examples.html

În fişierul obţinut, înlocuim elementele H1 (precum "<h1 id="clipping-text-to-a-box">Clipping Text to a Box</h1>") cu H2 (astfel, exemplele de programe PS date vor deveni sub-secţiuni) şi eliminăm (folosind iarăşi perl -pie) paragrafele în care erau referite fişierele sursă PS (precum "<p><a href="newsprint.ps">Try it.</a></p>"); apoi inserăm (prin Copy&Paste) fragmentul astfel modificat, în fgps.html - să zicem, imediat după lista <ul> de sub "Index of Examples".

Mai avem de înlocuit în fgps.html, referinţele la "examples/XXX.html" prin referinţele corespunzătoare din fragmentul HTML tocmai inserat; de exemplu, trebuie să înlocuim 'href="examples/newsprint.html"' din primul link al listei din secţiunea "Index of Examples" (şi din alte link-uri în care apare) cu 'href="#clipping-text-to-a-box"' (care identifică primul exemplu, din cadrul fragmentului tocmai inserat). De data aceasta am evitat surprizele (mai ales că o comandă de înlocuire ar fi acum şi complicat de formulat) şi am procedat manual, folosind de 13 ori meniul "Find and Replace..." din gedit.

8. Alte corecturi

Să căutăm în fgps.html ce referinţe la fişiere HTML au rămas nemodificate:

vb@Home:~/4apr/postscript$ \
>        sed -n 's/.*href="\([^"]*\.html\).*/\1/p'  fgps.html | sort --unique
books.html
contacting.html
draft.html
escapes.html
galley.html
graphics.html
http://www.tailrecursive.org/postscript/postscript.html
language.html
nup.html
opeators.html
verbotten.html

opeators.html în loc de operators.html e o greşeală de scriere a autorului site-ului, întâmplată în fişierul nup.html:

    <p>You may also notice that I wrap a <a href="opeators.html#gsave">gsave</a>
      and a <a href="operators.html#grestore">grestore</a> around the page and
      the additional code? The reason for this is ... </p>

Corectăm imediat în fgps.html, înlocuind (conform §6) cu href="#operator-gsave".

"galley.html" corespunde subsecţiunii "Galley Proofs" din secţiunea "Funky Stuff" şi din TOC, vedem că trebuie să înlocuim cu "#galley-proofs". La fel, înlocuim "nup.html" prin "#two-up". Însă pentru "draft.html", în TOC avem:

<li><a href="#draft"><span id="draft">Draft</span></a></li>

ceea ce este dubios: href indică elementul <span> conţinut în <a> (încât click pe link-ul respectiv nu va duce nicăieri). Totodată, titlul secţiunii este:

<h2 id="draft"><span id="draft">Draft</span></h2>

ceea ce este iarăşi dubios: avem două elemente cu un acelaşi ID (este drept că în fişierul iniţial "draft.html" avem '<h2><a name="draft">Draft</a></h2>'; deci pandoc a făcut o serie de conversii de elemente, care iată că mai trebuie "reparate". Dar pandoc a procedat corect: în HTML5 nu este permis atributul name pentru <a>, care există în fişierele iniţiale).

Corectăm punând în loc de "#draft" să zicem, "#the-draft" (cu care înlocuim şi "draft.html") şi înlocuind (în elementul H2) id="draft" cu id="the-draft" (ulterior am observat o "rezolvare" mult mai simplă…).

După ce am făcut aceste corecturi se cuvine să verificăm, relansând comanda sed redată mai sus; constatăm că încă apare "galley.html" şi căutând în fgps.html găsim "<a href="galley.html#hard">galley proofs</a>"; uitându-ne în TOC, găsim că trebuie să înlocuim cu href="#the-hard-part".

pandoc generează TOC ("prin program") şi este normal să ne aşteptăm că există încă o serie de itemi de aceeaşi natură cu cel "rezolvat" mai sus pentru "Draft" - de exemplu:

<li><a href="#principles"><span id="principles">Principles</span></a></li>
...
<li><a href="#fonts"><span id="fonts">Fonts</span></a></li>
...
<li><a href="#rotate"><span id="rotate">Rotate</span></a></li>

Fiecare dintre aceste link-uri este "nefuncţional", fiindcă vizează un element <span> conţinut în elementul <a> respectiv (al cărui href indică ID-ul <span>-ului). Cea mai simplă rezolvare constă în "curăţarea" acestor <span>-uri (care şi sunt inutile): ca să nu greşim, copiem TOC-ul din fgps.html într-un fişier nou şi pe acesta aplicăm o comandă perl -pie prin care să eliminăm perechile '<span id="...">' şi '</span>' (şi copiem rezultatul peste TOC-ul iniţial din fgps.html).

9. Alte îndreptări; completări

Verificăm iarăşi, ce referinţe la fişiere postscript/*.html au rămas în fgps.html:

vb@Home:~/4apr/postscript$ \
>        sed -n 's/.*href="\([^"]*\.html\).*/\1/p'  fgps.html | sort --unique
books.html
contacting.html
escapes.html
graphics.html
language.html
postscript.html
verbotten.html

Cu "graphics.html" rezolvăm imediat: avem o singură referinţă, anume '<a href="graphics.html#ctm">current transformation matrix</a>' şi este suficientă trunchierea href="#ctm". La fel rezolvăm cu "language.html": avem referinţe de forma '<a href="language.html#dictionary">dictionaries</a>' şi este suficientă eliminarea "prefixului" din href. Şi la fel rezolvăm cu referinţa href="postscript.html#note (de observat "surpriza" oferită acum de sed, care n-a dat "postscript.html" şi la execuţia din §8).

Celelalte patru fişiere n-au fost indicate în sections.txt, la §4; le-am putea alipi la fgps.html folosind iarăşi pandoc, dar preferăm să o facem manual (dacă este cazul; de exemplu, ignorăm aici fişierul "mailme.html", vizat din "contacting.html"; iar "verbotten.html" este dezvoltat de fapt în "eps.html"), cum arătăm mai jos pentru unul dintre ele.

Deschidem în gedit fişierul "books.html", copiem conţinutul din <body> şi-l pastăm în fgps.html la sfârşit, operând această modificare: adăugăm în titlul H1 al noii secţiuni 'id="ps-books"'; apoi, în cadrul TOC-ului înscriem la sfârşit un element <li><a href="#ps-books">PostScript Books<a></li>. Mai avem de modificat referinţa '<a href="books.html" id="2">books</a>' care apare undeva în fgps.html, punând href="#ps-books" (şi eliminăm 'id="2"'); iar pentru celelalte referinţe la "books.html" existente este suficientă trunchierea href-ului.

10. Validarea fişierului HTML

Fiindcă în §6–§9 am făcut aşa de multe modificări (câteva prin editare manuală, altele prin scripturi Perl sau Sed - în ambele cazuri fiind posibile şi scăpări sau greşeli), se cuvine să apelăm la Markup Validation Service, pentru a vedea ce "reparaţii" ar mai fi de făcut pe fişierul fgps.html.

Mai puţin luăm seama, la diverse atenţionări: <acronym> (utilizat de 24 de ori) este scos deja din HTML5, trebuind înlocuit cu <abbr>; atributul 'name' nu mai este valabil pentru <a> (de exemplu, din "books.html" preluasem '<a name="redbook"></a>'); etc.

Erorile importante sunt cele de tipul "Duplicate ID"; astfel (folosind grep), găsim clipping-to-text de două ori ca ID (marcând o secţiune H2 referită din TOC, respectiv una referită din "Index of Examples") şi de trei ori în href:

<li><a href="#clipping-to-text">Clipping to Text</a></li>  # în TOC
<h2 id="clipping-to-text">Clipping to Text</h2>  # secţiunea "Clipping" referită din TOC
<p>... <a href="#clipping-to-text">example</a> ... </p>  # referire la exemplul "Clipping"
<li><a href="#clipping-to-text">Clipping to text</a></li>  # în "Index of Examples"
<h2 id="clipping-to-text">Clipping to Text</h2>  # exemplul referit din "Index of Examples"

Alegem să corectăm cele două elemente care ţin de TOC (link-ul din TOC şi ID-ul secţiunii H2 asociate acestuia), adăugând prefixul sect- ("sect-clipping-to-text").

Avem o situaţie similară pentru drawing-a-box" (cu ghilimele la sfârşit, pentru ca grep să evite drawing-a-box-with-rlineto, care apare deasemenea ca referinţă) şi procedăm la fel, adăugând prefixul "sect-" în link-ul din TOC şi în ID-ul secţiunii asociate acestui link. Absolut la fel procedăm şi pentru translate, respectiv scale - după aceea constatăm că serviciul de validare nu ne mai semnalează erori (ci doar "Warning").

11. Conversia în LaTeX (apoi, în PDF) a fişierului HTML

Înfiinţăm o "versiune de lucru" pentru fgps.html, copiindu-l în directorul părinte (împreună cu "postscript.css"). Deschidem 4apr/fgps.html în gedit, ştergem TOC-ul (diviziunea <nav id="TOC">), salvăm şi apoi tastăm în terminal comanda pentru conversie la fişier LaTeX (optând pentru generarea unui TOC cu un singur nivel, pentru colorarea referinţelor interne şi pentru inserarea ca "note de subsol" a link-urilor externe):

vb@Home:~/4apr$ pandoc -s  -f html -t latex --css=postscript.css \
> --toc --toc-depth=1 \
> -V colorlinks  -V links-as-notes \
> -o fgps.tex  fgps.html

Pe fişierul obţinut invocăm un compilator de LaTeX, obţinând documentul PDF fgps.pdf:

vb@Home:~/4apr$ pdflatex fgps.tex

Pentru generarea corectă a referinţelor încrucişate, comanda de compilare trebuie executată încă o dată. Vizualizând fgps.pdf ne vom putea da seama ce modificări ar mai fi de făcut pe fgps.tex, încât reluând compilarea, să obţinem un document PDF mai bine organizat şi acceptabil formatat.

Deschizând fgps.pdf - fie cu evince, fie cu "Atril Document Viewer", fie cu Firefox - constatăm că link-urile interne sunt "clickabile" (funcţionează la fel ca în cazul fgps.html); acest fapt este urmarea includerii în şablonul LaTeX folosit de pandoc a pachetului hyperref (desigur, pe versiunea tipărită pe hârtie link-urile respective sunt "nefuncţionale", fiind doar marcate printr-o culoare distinctă).

pandoc a montat TOC chiar la început; putem căuta comanda \tableofcontents şi putem reamplasa blocul care o conţine, de exemplu între subsecţiunile "About this Document" şi "Note" (respectând astfel, poziţionarea "Contents" existentă pe site-ul original).

Ar fi de făcut câteva operaţii simple, pentru ca TOC să nu difere prea mult de cel aflat pe site-ul HTML original. În fgps.tex, "Index of Operators" este declarată ca "\subsection" (sub-secţiune a secţiunii "PostScript Operators"); pentru ca în TOC (care fusese generat cu "--toc-depth=1", deci conţine numai secţiuni) să apară "Index of Operators" şi să nu apară "PostScript Operators", căutăm în fgps.tex "\section{PostScript Operators}" şi modificăm în "section*" (sufixul "*" va determina ca secţiunea respectivă să nu mai apară în TOC); apoi, pentru "Index of Operators" punem "\section", în loc de "\subsection". Deasemenea, adăugăm sufixul "*" în declaraţia secţiunilor "Errors You Might Encounter", "String Escape Codes", şi alte câteva care urmează după ele, exceptând "Frequently Asked Questions" (pe care eventual, o mutăm înaintea secţiunilor sufixate cu "*").

Ceva mai mult am avea de muncă legat de "formatare": în fgps.log, compilatorul a înregistrat liniile din fgps.tex pentru care boxa TeX asociată liniei respective este mai largă decât lăţimea de bază a boxei asociate liniei de text - de exemplu,

Overfull \hbox (27.74892pt too wide) in paragraph at lines 685--685
[]        \T1/lmtt/m/n/10 72 72 moveto            % Lower left corner of text a
t (72, 72)[] 

Aproape toate atenţionările "Overfull \hbox" de aici vizează secvenţele de cod PS redate sub \begin{verbatim} şi corectura de făcut ar reveni cel mai adesea la tăierea primelor spaţii de pe fiecare linie, sau a unor spaţii care preced un "#" (comentariu).
Desigur, mai bine ar fi de încercat vreun pachet suplimentar, specializat pe redarea codului-sursă de program. Am preferat totuşi să las secvenţele de cod aşa cum sunt; doar că am redus puţin, mărimea caracterelor din cadrul acestor secvenţe: folosind "Find and Replace..." din gedit, am înlocuit \begin{verbatim} prin {\small\begin{verbatim} şi \end{verbatim} prin \end{verbatim}}.

Compilând cu xelatex, am constatat şi reducerea dimensiunii fişierului PDF rezultat, cam la jumătate din cea obţinută cu pdflatex.

În secţiunea "Raster Graphics" sunt redate trei tabele, în legătură cu operatorii PS image şi colorimage; acestea ar trebui modificate cumva, pentru a arăta ca pe site-ul original - dar intenţionez să le înlocuiesc şi rezerv aceasta pentru un alt articol; documentul PDF constituit mai sus este fgps.pdf.

vezi Cărţile mele (de programare)

docerpro | Prev | Next