%%%----------------------------------------------------------------------
%%% File : sdocbook2xhtml.erl
%%% Description : Erlang XSLT like "stylesheet" for exporting
%%% Simplified Docbook XML to XHTML.
%%%
%%% Modules used : lists, io_lib, xmerl, xmerl_lib, xmerl_xs
%%%
%%%----------------------------------------------------------------------
-module(sdocbook2xhtml).
-author('mikael.karlsson@creado.com').
-include("xmerl.hrl").
-import(xmerl_lib, [markup/3,mapxml/2, foldxml/3, mapfoldxml/3]).
-import(xmerl_xs, [ xslapply/2, value_of/1, select/2, built_in_rules/2]).
-export([ process_xml/1 ]).
-export([abbrev/4,
abstract/4,
acronym/4,
address/4,
anchor/4,
appendix/4,
appendixinfo/4,
article/4,
articleinfo/4,
audiodata/4,
audioobject/4,
author/4,
authorgroup/4,
authorinitials/4,
bibliography/4,
bibliomixed/4,
bibliomisc/4,
bibliomset/4,
biblioset/4,
blockquote/4,
caption/4,
citetitle/4,
city/4,
colspec/4,
command/4,
computeroutput/4,
copyright/4,
corpauthor/4,
country/4,
date/4,
edition/4,
editor/4,
email/4,
emphasis/4,
entry/4,
example/4,
fax/4,
figure/4,
filename/4,
firstname/4,
footnote/4,
holder/4,
honorific/4,
imagedata/4,
imageobject/4,
informaltable/4,
inlinemediaobject/4,
isbn/4,
issn/4,
issuenum/4,
legalnotice/4,
lineage/4,
link/4,
literal/4,
itemizedlist/4,
listitem/4,
mediaobject/4,
member/4,
note/4,
orderedlist/4,
othercredit/4,
othername/4,
para/4,
phone/4,
phrase/4,
programlisting/4,
publishername/4,
quote/4,
replaceable/4,
revhistory/4,
revision/4,
revnumber/4,
revremark/4,
row/4,
section/4,
sectioninfo/4,
simplelist/4,
subtitle/4,
surname/4,
systemitem/4,
table/4,
tbody/4,
term/4,
tfoot/4,
tgroup/4,
thead/4,
title/4,
titleabbrev/4,
trademark/4,
ulink/4,
userinput/4,
variablelist/4,
varlistentry/4,
xref/4,
year/4
]).
xmlhead() -> "<\?xml version=\"1.0\" encoding=\"iso-8859-1\"\?>".
doctype() ->"\n".
style() ->
"".
process_xml(E)->
%% lists:flatten(template( E )).
template( E ).
%% article is the root element
template(E0 = #xmlElement{name=article})->
E = changetitle(E0), %% Add section numbering to titles
[ xmlhead(), doctype(),
""
"
"
"",
value_of(select("articleinfo/title",E)),
"",
style(),
""
"",
%% "",
xslapply( fun template/1, select("articleinfo",E)),
process_toc(E), %% Insert toc between info and main part of article
xslapply( fun template/1, select("section",E)),
xslapply( fun template/1, select("appendix",E)),
""];
template(E = #xmlElement{name=Name})->
A = xslapply( fun template/1, E),
case catch
sdocbook2xhtml:Name(A, E#xmlElement.attributes, E#xmlElement.parents,E)
of
{'EXIT', {undef, _}} ->
A;
{'EXIT', Reason} ->
exit(Reason);
Res ->
Res
end;
template(E) ->
built_in_rules( fun template/1, E).
%% -------------------------------------------------------------------
%% simple serialize tags
abbrev(Data, Attrs, [{bibliomset,_}|_], E)->
["", Data, ""];
abbrev(Data, Attrs, Parents, E)->
markup("abbr",Attrs, Data).
abstract(Data, Attrs, Parents, E)->
["Abstract
", Data, "
"].
acronym(Data, Attrs, Parents, E)->
markup("acronym",Attrs, Data).
address(Data, Attrs, Parents, E)->
markup("address", Attrs, Data).
anchor(Data, Attrs, Parents, E)->
case find_attribute(id, Attrs) of
{value,ID} ->
["", Data, ""];
false ->
Data
end.
appendix(Data, Attrs, Parents, E)->
["Appendix
", Data].
appendixinfo(Data,_,_,_)->
Data.
article(Data, Attrs, Parents, E)->
[""
"",
Data,
""].
articleinfo(Data,_,_,_)->
Data.
audiodata(Data, Attrs, Parents, E)->Data.
audioobject(_,_,_,_)->
[].
author(Data, Attrs, [{authorgroup,_} | _], E)->
markup("dd", Attrs, Data);
author(Data, Attrs, Parents, E)->
Data.
authorgroup(Data,_,_,_)->
["- Author
",Data,"
"].
authorinitials(Data,_,_,_)-> Data.
bibliography(Data, Attrs, Parents, E)->
["Bibliography
" ,Data].
bibliomisc(Data,_,_,_)-> Data.
bibliomixed(Data,_,_,_)-> ["",Data, "
"].
bibliomset(Data,_,_,_)-> [Data, ""].
biblioset(Data,_,_,_)-> Data.
blockquote(Data, Attrs, Parents, E)->
markup("blockquote",Attrs, Data).
caption(Data, Attrs, Parents, E)-> Data.
citetitle(Data,_,_,_)-> ["",Data,""].
city(Data,_,_,_)->
Data.
%% Fix Me is it "col" element in html?
colspec(_, Attrs,_,_)->
[].
command(Data,_,_,_)->
["", Data, ""].
computeroutput(Data,_,_,_)->
["", Data, ""].
copyright(Data,_,_,_)->
[ "© ", Data].
corpauthor(Data,_,_,_)->
Data.
country(Data,_,_,_)->
Data.
date(Data,_,[{revision,_}|_],_)->
["", Data, " | "];
date(Data,_,_,_)->
Data.
edition(Data,_,_,_)->
Data.
editor(Data,_,_,_)->
Data.
email(Data,_,_,_)->
["",Data,""].
emphasis(Data, Attrs, Parents, E)->
["", Data, ""].
%% Cell in a table
entry(Data, Attrs, [{row,_}, {thead,_} | _], E)->
["", Data, " | "];
entry(Data, Attrs, Parents, E)->
["", Data, " | "].
example(Data, Attrs, Parents, E)->
["
", Data, "
"].
fax(Data, Attrs, Parents, E)->
["", Data, ""].
%% May contain ulink to image, resolved by ulink type
figure(Data, _, _, _)->
Data.
filename(Data, _, _, _)->
["", Data, ""].
firstname(Data, _, _, _)->
[Data , " " ].
footnote(Data, _, _, _)->
Data.
holder(Data, _, _, _)->
[" ",Data].
honorific(Data, _, _, _)->
Data.
imagedata(Data, Attrs, Parents, E)->
SRC =
case find_attribute(fileref, Attrs) of
{value,AS} ->
" src=" ++ AS ++ " ";
false ->
[]
end,
ALT =
case SRC of
[] ->
" alt=\"No image!\" ";
_ ->" alt=\"" ++ SRC ++ "\" "
end,
WIDTH =
case find_attribute(width, Attrs) of
false ->
[];
{value,A} ->" width=" ++ A ++ " "
end,
[""].
imageobject(Data, Attrs, Parents, E)->
Data.
informaltable(Data, Attrs, Parents, E)->
[""].
inlinemediaobject(Data, Attrs, Parents, E)->
Data.
isbn(Data, Attrs, Parents, E)->
Data.
issn(Data, Attrs, Parents, E)->
Data.
issuenum(Data, Attrs, Parents, E)->
Data.
itemizedlist(Data, Attrs, Parents, _)->
markup("ul", Attrs, Data).
%keyword
%{
% display: inline;
%}
%keywordset
%{
% display: inline;
%}
legalnotice(Data, Attrs, Parents, _)->
markup("small", Attrs, Data).
lineage(Data, Attrs, Parents, _)->
Data.
%lineannotation
%{
% display: inline;
%}
% Hypertext link
link(Data, Attrs, Parents, _)->
case find_attribute(linkend, Attrs) of
{value,LINK} ->
["", Data, ""];
false ->
Data
end.
listitem(Data, Attrs, [{varlistentry,_} | _], E) ->
markup("dd", Attrs, Data);
listitem(Data, Attrs, Parents, _)->
markup("li", Attrs, Data).
literal(Data, Attrs, Parents, _)->
markup("tt", Attrs, Data).
%literallayout
%{
% display: inline;
%}
mediaobject(Data, Attrs, Parents, _)->
Data.
%% simplelist member
member(Data, Attrs, Parents, _)->
[Data,"
"].
note(Data, Attrs, Parents, _)->
[""].
%objectinfo
%{
% display: inline;
%}
%option
%{
% display: inline;
%}
orderedlist(Data, Attrs, Parents, _)->
markup("ol",Attrs,Data).
%% Hmm otheraddr not in DTD
%otheraddr
%{
% display: inline;
%}
othercredit(Data, Attrs, Parents, _)->Data.
othername(Data, Attrs, Parents, E)->Data.
%% IGNORE
%pagenums
%{
% display: inline;
%}
para(Data, Attrs, [{listitem,_}|_], E)->
Data;
para(Data, Attrs, [{note,_}|_], E)->
Data;
para(Data, Attrs, Parents, E)->
markup("p", Attrs, Data).
phone(Data, Attrs, Parents, E)->Data.
phrase(Data, Attrs, Parents, E)->Data.
%pob
%{
% display: inline;
%}
%postcode
%{
% display: inline;
%}
%printhistory
%{
% display: inline;
%}
%procedure
%{
% display: inline;
%}
programlisting(Data, Attrs, Parents, E)->
[""].
%pubdate
%{
% display: inline;
%}
%publisher
%{
% display: inline;
%}
publishername(Data, Attrs, Parents, E)->
Data.
quote(Data, Attrs, Parents, _)->
markup("q", Attrs, Data).
replaceable(Data, Attrs, Parents,_)->
markup("i", Attrs, Data).
revhistory(Data, Attrs, Parents,E)->
{A,B,C} = case E#xmlElement.language of
"en" -> {"Revision history","Date","Comment"};
"sv" -> {"Revisionshistoria","Datum","Kommentar"};
_ ->{"lang is undefined","lang is undefined","lang is undefined"}
end,
["",A,"
",""
"Rev. | ",B," | ",C," |
", Data,
"
"].
revision(Data, Attrs, Parents,_)->
markup("tr", Attrs, Data).
revnumber(Data, Attrs, Parents,_)->
markup("td", Attrs, Data).
revremark(Data, Attrs, Parents,_)->
markup("td", Attrs, Data).
row(Data, Attrs, Parents, E)->
markup("tr", Attrs, Data).
section(Data, Attrs, Parents, E)->
Data.
sectioninfo(Data, Attrs, Parents, E)->Data.
%sidebar
%{
% display: block;
%}
simplelist(Data, Attrs, Parents, E)->
[""].
%state
%{
% display: inline;
%}
%step
%{
% display: inline;
%}
%street
%{
% display: inline;
%}
%substeps
%{
% display: inline;
%}
subtitle(Data, Attrs, Parents, E)->
["", Data, "
"].
surname(Data, Attrs, Parents, E)->Data.
systemitem(Data, Attrs, Parents, E)->
markup("b", Attrs, Data).
table(Data, Attrs, Parents, E)->
[""].
%% Fix me alot
tbody(Data, Attrs, Parents, E)->
markup("tbody", Attrs, Data).
%{
% display: table-row-group;
%}
term(Data, Attrs, [{varlistentry,_} | _], E) ->
markup("dt", Attrs, Data).
%textobject
%{
% display: inline;
%}
tfoot(Data, Attrs, Parents, E)->
markup("tfoot",Attrs, Data).
%% Fixme alot
tgroup(Data, Attrs, Parents, E)->
markup("colgroup", Attrs, Data).
%{
% display: table;
%}
thead(Data, Attrs, Parents, E)->
markup("thead",Attrs, Data).
%{
% display: table-row-group;
%}
title(Data, Attrs, Parents, E)->
%% io:fwrite("Parents ~p~n", [Parents]),
title1(Data, Attrs, Parents, E).
title1(Data, Attrs, [{section,_}, {section,_}, {section,_},
{section,_}, {section,_}, {appendix,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {section,_}, {section,_},
{section,_}, {appendix,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {section,_}, {section,_},
{apendix,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {section,_}, {appendix,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {appendix,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{appendix,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {section,_}, {section,_},
{section,_}, {section,_}, {section,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {section,_}, {section,_},
{section,_}, {section,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,_}, {section,_}, {section,_},
{section,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{section,C}, {section,B}, {section,A} | _], E) ->
{value, Id} = find_attribute(id,Attrs),
["", Data, "
"];
title1(Data, Attrs, [{section,B}, {section,A} | _], E) ->
{value, Id} = find_attribute(id,Attrs),
["", Data, "
"];
title1(Data, Attrs, [{section,A} | _], E) ->
{value, Id} = find_attribute(id,Attrs),
["", Data, "
"];
title1(Data, Attrs, [{articleinfo,_} | _], E) ->
["", Data, "
"];
title1(Data, Attrs, [{table,_} | _], E) ->
["", Data, ""];
title1(Data, Attrs, [{bibliomset,_} | _], E) ->
["", Data, ""];
title1(Data, Attrs, Parents, E)->
["", Data, "
"].
titleabbrev(Data, Attrs, Parents, E)->[].
trademark(Data, Attrs, Parents, E)->
[ Data, " ® "].
ulink(Data, Attrs, Parents, E)->
case find_attribute(url, Attrs) of
{value,LINK} ->
["", Data, ""];
false ->
Data
end.
%% User input is Constant Bold
userinput(Data, Attrs, Parents, E)->
["", Data, ""].
variablelist(Data, Attrs, Parents, E)->
markup("dl", Attrs, Data).
varlistentry(Data, Attrs, Parents, E)->Data.
%videodata
%{
% display: inline;
%}
%videoobject
%{
% display: inline;
%}
%volumenum
%{
% display: inline;
%}
xref(Data, Attrs, Parents, E)->
case find_attribute(linkend, Attrs) of
{value,LINK} ->
[""];
false ->
Data
end.
year(Data, Attrs, Parents, E)->Data.
%% ----------------------------------------------------------
%% Utils find_attribute copied from Ulf Wigers xmerl distribution
find_attribute(Name, Attrs) ->
case lists:keysearch(Name, #xmlAttribute.name, Attrs) of
{value, #xmlAttribute{value = V}} ->
{value, V};
false ->
false
end.
%% ------------
changetitle(A) ->
Afun = fun changecount/2,
{E, Acc} = mapfoldxml(Afun, {0,0,0,0,0,0}, A),
E.
changecount(#xmlElement{name=title}=E, {A,B,C,Ex,Fig,Tab})->
case E#xmlElement.parents of
[{example,_} |_] ->
{addexhead(E,{A,Ex+1}), {A,B,C,Ex+1,Fig,Tab} };
[{figure,_} |_] ->
{addfighead(E,{A,Fig+1}), {A,B,C,Ex,Fig+1,Tab} };
[{table,_} |_] ->
{addtablehead(E,{A,Tab+1}), {A,B,C,Ex,Fig,Tab+1} };
[{section,_},{section,_},{section,_},{article,_} |_] ->
{addheader(E,{A,B,C+1}), {A,B,C+1,Ex,Fig,Tab} };
[{section,_},{section,_},{article,_} |_] ->
{ addheader(E,{A,B+1,0}), {A,B+1,0,Ex,Fig,Tab} };
[{section,_},{article,_} |_] ->
{addheader(E,{A+1,0,0}),{A+1,0,0,0,0,0}};
_ ->
{E,{A,B,C,Ex,Fig,Tab}}
end;
changecount(E, Acc)->{E,Acc}.
addexhead(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, {Ch,No})->
NewHeader = "Example " ++
integer_to_list(Ch)++" - "++ integer_to_list(No) ++
" " ++ T1#xmlText.value,
E#xmlElement{content=[T1#xmlText{value=NewHeader}]}.
addfighead(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, {Ch,No})->
NewHeader = "Figure " ++
integer_to_list(Ch)++" - "++ integer_to_list(No) ++
" " ++ T1#xmlText.value,
E#xmlElement{content=[T1#xmlText{value=NewHeader}]}.
addtablehead(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, {Ch,No})->
NewHeader = "Table " ++
integer_to_list(Ch)++" - "++ integer_to_list(No) ++
" " ++ T1#xmlText.value,
E#xmlElement{content=[T1#xmlText{value=NewHeader}]}.
addheader(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, Chapters)->
NewHeader = chapterstring(Chapters)++ " " ++ T1#xmlText.value,
NewAtts = addid(E#xmlElement.attributes, Chapters),
E#xmlElement{content=[T1#xmlText{value=NewHeader}],
attributes = NewAtts}.
chapterstring({A,0,0})->integer_to_list(A);
chapterstring({A,B,0})->integer_to_list(A)++"."++ integer_to_list(B);
chapterstring({A,B,C})->integer_to_list(A) ++ "." ++
integer_to_list(B) ++ "." ++
integer_to_list(C).
%% addid add id attribute if it not already exists
addid(OldAtts, Chapters)->
case find_attribute(id, OldAtts) of
{value,_} ->
OldAtts;
false ->
add_attribute(id,"sect_"++ chapterstring(Chapters),
OldAtts)
end.
add_attribute(Name, Value, OldAtts)->
[#xmlAttribute{ name=Name, value = Value}| OldAtts ].
process_toc(E)->
AFun = fun chapindex/2,
TOCR = foldxml(AFun, [], E),
% Str = case find_attribute(lang, E#xmlElement.attributes) of
% {value,"en"} -> "Table of Contents";
% {value,"sv"} -> "Innehållsförtecking";
% _ ->"lang is undefined"
% end,
Str = case E#xmlElement.language of
"en" -> "Table of Contents";
"sv" -> "Innehållsförtecking";
_ ->"lang is undefined"
end,
TOC = ["",Str,"
",
lists:reverse(TOCR), "
"].
chapindex(#xmlElement{name=title}=E, Accu)->
case E#xmlElement.parents of
[{section,_},{section,_},{section,_},{article,_} |_] ->
[""++spind(3)++ addlink(E,"toc_level_3") ++""| Accu];
[{section,_},{section,_},{article,_} |_] ->
[""++spind(2)++ addlink(E,"toc_level_2") ++ ""| Accu];
[{section,_},{article,_} |_] ->
[""++spind(1)++ addlink(E,"toc_level_1") ++""| Accu];
_ ->
Accu
end;
chapindex(E, Accu) ->
Accu.
spind(0) ->"";
spind(X)->
" " ++ spind(X-1).
addlink(E, TocLevel)->
{value,LINK} = find_attribute(id,E#xmlElement.attributes),
[#xmlText{value=Title}|_] = E#xmlElement.content, %% Pfuii
"" ++
Title ++ "".