GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W GAPDoc2Text.gi GAPDoc Frank Lübeck
##
##
#Y Copyright (C) 2000, Frank Lübeck, Lehrstuhl D für Mathematik,
#Y RWTH Aachen
##
## The files GAPDoc2Text.g{d,i} contain a conversion program which
## produces from a GAPDoc XML-document a text version for viewing the
## document on screen (GAP online help).
##
##
## We add a link to the document root to all recursively called functions
## by adding .root entries. The toc-, index- and bib-information is
## collected in the root.
##
## The set of elements is partitioned into two subsets - those which
## contain whole paragraphs and those which don't.
##
## The handler of a paragraph containing element (see
## GAPDoc2TextProcs.ParEls below) gets a list as argument to which it adds
## entries pairwise: the first of such a pair is the paragraph counter
## (like [3,2,1,5] meaning Chap.3, Sec.2, Subsec.1, Par.5) and the second
## is the formatted text of this paragraph.
##
## Some handlers of paragraph containing elements do the formatting
## themselves (e.g., .List), the others are handled in the main recursion
## function 'GAPDoc2TextContent'.
##
## We produce a full version of the document in text format, including
## title page, abstract and other front matter, table of contents,
## bibliography (via BibTeX-data files) and index. Highlighting with
## colors for ANSI color sequence capable terminals is included.
##
## In this text converter we also produce the manual.six data. For
## getting page numbers in .dvi and .pdf versions, the LaTeX-converter
## and LaTeX must be run first. This produces a .pnr file.
##
InstallValue(GAPDoc2TextProcs, rec());
## Some text attributes for display on ANSI terminals
## We do all the formatting with not used escape sequences of form
## <ESC>[<num>X. These are substituted by user configurable sequences
## before an actual display.
##
## the actual themes are in the file TextThemes.g
##
## <#GAPDoc Label="SetGAPDocTextTheme">
## <ManSection >
## <Func Arg="[optrec1[, optrec2] ...]" Name="SetGAPDocTextTheme" />
## <Returns>nothing</Returns>
## <Description>
## This utility function is for readers of the screen version of &GAP;
## manuals which are generated by the &GAPDoc; package. It allows to
## configure the color and attribute layout of the displayed text.
## There is a default which can be reset by calling this function
## without argument. <P/>
##
## As an abbreviation the arguments <A>optrec1</A> and so on can be
## strings for the known name of a theme. Information about valid
## names is shown with <C>SetGAPDocTextTheme("");</C>. <P/>
##
## Otherwise, <A>optrec1</A> and so on must be a record. Its entries
## overwrite the corresponding entries in the default and in previous
## arguments. To construct valid markup you
## can use <Ref Var="TextAttr"/>. Entries must be either pairs of
## strings, which are put before and after the corresponding text, or
## as an abbreviation it can be a single string. In the latter case,
## the second string is implied; if the string contains an escape
## sequence the second string is <C>TextAttr.reset</C>, otherwise the
## given string is used. The following components are recognized:
##
## <List>
## <Mark><C>flush</C></Mark><Item><C>"both"</C> for left-right justified
## paragraphs, and <C>"left"</C> for ragged right ones</Item>
## <Mark><C>Heading</C></Mark><Item>chapter and (sub-)section headings </Item>
## <Mark><C>Func</C></Mark><Item>function, operation, ... names </Item>
## <Mark><C>Arg</C></Mark><Item>argument names in descriptions</Item>
## <Mark><C>Example</C></Mark><Item>example code</Item>
## <Mark><C>Package</C></Mark><Item>package names</Item>
## <Mark><C>Returns</C></Mark><Item>Returns-line in descriptions</Item>
## <Mark><C>URL</C></Mark><Item>URLs</Item>
## <Mark><C>Mark</C></Mark><Item>Marks in description lists</Item>
## <Mark><C>K</C></Mark><Item>&GAP; keywords</Item>
## <Mark><C>C</C></Mark><Item>code or text to type</Item>
## <Mark><C>F</C></Mark><Item>file names</Item>
## <Mark><C>B</C></Mark><Item>buttons</Item>
## <Mark><C>M</C></Mark><Item>simplified math elements</Item>
## <Mark><C>Math</C></Mark><Item>normal math elements</Item>
## <Mark><C>Display</C></Mark><Item>displayed math elements</Item>
## <Mark><C>Emph</C></Mark><Item>emphasized text</Item>
## <Mark><C>Q</C></Mark><Item>quoted text</Item>
## <Mark><C>Ref</C></Mark><Item>reference text</Item>
## <Mark><C>Prompt</C></Mark><Item>&GAP; prompt in examples</Item>
## <Mark><C>BrkPrompt</C></Mark><Item>&GAP; break prompt in examples</Item>
## <Mark><C>GAPInput</C></Mark><Item>&GAP; input in examples</Item>
## <Mark><C>reset</C></Mark><Item>reset to default, don't change this </Item>
## <Mark><C>BibAuthor</C></Mark><Item>author names in bibliography</Item>
## <Mark><C>BibTitle</C></Mark><Item>titles in bibliography</Item>
## <Mark><C>BibJournal</C></Mark><Item>journal names in bibliography</Item>
## <Mark><C>BibVolume</C></Mark><Item>volume number in bibliography</Item>
## <Mark><C>BibLabel</C></Mark><Item>labels for bibliography entries</Item>
## <Mark><C>BibReset</C></Mark><Item>reset for bibliography,
## don't change</Item>
## <Mark><C>ListBullet</C></Mark><Item>bullet for simple lists (2
## visible characters long)</Item>
## <Mark><C>EnumMarks</C></Mark><Item>one visible character before and
## after the number in enumerated lists</Item>
## <Mark><C>DefLineMarker</C></Mark><Item>marker before function and variable
## definitions (2 visible characters long)</Item>
## <Mark><C>FillString</C></Mark><Item>for filling in definitions and
## example separator lines</Item>
## </List>
##
## <Example>
## gap> # use no colors for GAP examples and
## gap> # change display of headings to bold green
## gap> SetGAPDocTextTheme("noColorPrompt",
## > rec(Heading:=Concatenation(TextAttr.bold, TextAttr.2)));
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
# used fields
GAPDoc2TextProcs.TextAttrFields := [ "reset", "Heading", "Func", "Arg",
"Example", "Package", "Returns", "URL", "Mark", "K", "C", "F",
"B", "Emph", "Ref", "BibReset", "BibAuthor", "BibTitle",
"BibJournal", "BibVolume", "BibLabel", "Q", "M", "Math",
"Display", "Prompt", "BrkPrompt", "GAPInput", "GAPOutput",
"DefLineMarker", "ListBullet", "EnumMarks",
"FillString", "format", "flush" ];
## We produce a text document with markup that is ignored by terminals,
## this can be substituted on the fly before display according to the
## current GAPDocTextTheme.
GAPDoc2TextProcs.TextAttr := rec();
GAPDoc2TextProcs.TextAttr.f := function()
local i, l, fs;
fs := GAPDoc2TextProcs.TextAttrFields;
l := Length(fs);
# begin and end markers ESC[(i)X and ESC[(i+100)X
for i in [1..l] do
GAPDoc2TextProcs.TextAttr.(fs[i]) :=
[Concatenation(TextAttr.CSI, String(i-1), "X"),
Concatenation(TextAttr.CSI, String(100+i-1), "X")];
od;
end;
GAPDoc2TextProcs.TextAttr.f();
Unbind(GAPDoc2TextProcs.TextAttr.f);
GAPDoc2TextProcs.ParEls :=
[ "Display", "Example", "Log", "Listing", "List", "Enum", "Item", "Table",
"TitlePage", "Address", "TitleComment", "Abstract", "Copyright",
"Acknowledgements", "Colophon", "TableOfContents", "Bibliography", "TheIndex",
"Subsection", "ManSection", "Description", "Returns", "Section", "Chapter",
"Appendix", "Body", "Book", "WHOLEDOCUMENT", "Attr", "Fam", "Filt", "Func",
"Heading", "InfoClass", "Meth", "Oper", "Constr", "Prop", "Var", "Verb" ];
## arg: a list of strings
## nothing for now, may be enhanced and documented later.
SetGapDocTxtOptions := function(arg)
local gdp;
gdp := GAPDoc2TextProcs;
return;
end;
## here we collect paragraphs to whole chapters and remember line numbers
## of subsections for the .six information
GAPDoc2TextProcs.PutFilesTogether := function(l, six)
local countandshift, concat, files, n, i, p, a, tmp;
# count number of lines in txt and add 2 spaces in the beginning of
# each line, returns [newtxt, nrlines]
countandshift := function(txt)
local new, ind, n, p, pos;
# sometimes there may occur paragraph elements inside text elements
# (like list in list item) - we concatenate the text here.
concat := function(txt)
local new, a;
if not IsString(txt) then
new := "";
for a in txt do
if IsChar(a) then
Add(new, a);
elif IsList(a) then
Append(new, concat(a));
fi;
od;
else
new := txt;
fi;
ConvertToStringRep(new);
return new;
end;
txt := concat(txt);
new := " ";
ind := " ";
n := 0;
p := 0;
pos := Position(txt, '\n');
while pos <> fail do
Append(new, txt{[p+1..pos]});
if pos < Length(txt) then
Append(new, ind);
fi;
n := n+1;
p := pos;
pos := Position(txt, '\n', p);
od;
if p < Length(txt) then
Append(new, txt{[p+1..Length(txt)]});
fi;
return [new, n];
end;
# putting the paragraphs together (one string (file) for each chapter)
files := rec();
for n in Set(List([2,4..Length(l)], i-> l[i-1][1])) do
files.(n) := rec(text := "", ssnr := [], linenr := [], len := 0);
od;
for i in [2,4..Length(l)] do
n := files.(l[i-1][1]);
p := countandshift(l[i]);
if Length(n.ssnr)=0 or l[i-1]{[1..3]} <> n.ssnr[Length(n.ssnr)] then
Add(n.ssnr, l[i-1]{[1..3]});
Add(n.linenr, n.len+1);
fi;
Append(n.text, p[1]);
n.len := n.len + p[2];
od;
# - add line numbers to six information
# - add simplified strings for searching
# - add hash strings of the latter for links in HTML and PDF version
# (such that links remain stable as long as the (sub)section exists
# and stays in the same chapter)
Info(InfoGAPDoc, 1, "#I Producing simplified search strings and labels ",
"for hyperlinks . . .\n");
tmp := ShallowCopy(six);
SortParallel(List([1..Length(tmp)], i-> [tmp[i][3],i]), tmp);
for i in [1..Length(six)] do
a := tmp[i];
p := Position(files.(a[3][1]).ssnr, a[3]);
if p = fail then
Error("don't find subsection ", a[3], " in text documention");
fi;
Add(a, files.(a[3][1]).linenr[p]);
a[6] := SIMPLE_STRING(StripEscapeSequences(a[1]));
NormalizeWhitespace(a[6]);
# the 'X' is to start with a proper letter, since this will be used
# for ID type attributes; we use the same label for all entries with
# the same subsection number
if i > 1 and a[3] = tmp[i-1][3] then
a[7] := tmp[i-1][7];
else
a[7] := Concatenation("X", HexStringInt(CrcText(a[6])+2^31),
HexStringInt(CrcText(Reversed(a[6]))+2^31));
fi;
od;
return files;
end;
## <#GAPDoc Label="GAPDoc2Text">
## <ManSection >
## <Func Arg="tree[, bibpath][, width]" Name="GAPDoc2Text" />
## <Returns>record containing text files as strings and other
## information</Returns>
## <Description>
## The argument <A>tree</A> for this function is a tree
## describing a &GAPDoc; XML document as returned by <Ref
## Func="ParseTreeXMLString" /> (probably also checked with <Ref
## Func="CheckAndCleanGapDocTree" />). This function produces a text
## version of the document which can be used with &GAP;'s online
## help (with the <C>"screen"</C> viewer, see <Ref BookName="Ref"
## Func="SetHelpViewer" />). It includes title page, bibliography
## and index. The bibliography is made from BibXMLext or &BibTeX;
## databases, see <Ref Chap="ch:bibutil"/>.
## Their location must be given with the argument <A>bibpath</A> (as
## string or directory object).<P/>
##
## The output is a record with one component for each chapter
## (with names <C>"0"</C>, <C>"1"</C>, ..., <C>"Bib"</C> and
## <C>"Ind"</C>). Each such component is again a record with
## the following components:
##
## <List >
## <Mark><C>text</C></Mark>
## <Item>the text of the whole chapter as a string</Item>
## <Mark><C>ssnr</C></Mark>
## <Item>list of subsection numbers in this chapter (like <C>[3, 2,
## 1]</C> for chapter 3, section 2, subsection 1)
## </Item>
## <Mark><C>linenr</C></Mark>
## <Item>corresponding list of line numbers where the subsections
## start</Item>
## <Mark><C>len</C></Mark>
## <Item>number of lines of this chapter</Item>
## </List>
##
## The result can be written into files with the command <Ref
## Func="GAPDoc2TextPrintTextFiles" />.<P/>
##
## As a side effect this function also produces the
## <F>manual.six</F> information which is used for searching in
## &GAP;'s online help. This is stored in <C><A>tree</A>.six</C>
## and can be printed into a <F>manual.six</F> file with
## <Ref Func="PrintSixFile" /> (preferably after producing a
## &LaTeX; version of the document as well and adding the
## page number information to <C><A>tree</A>.six</C>, see <Ref
## Func="GAPDoc2LaTeX" /> and <Ref Func="AddPageNumbersToSix"
## />).<P/>
##
## The text produced by this function contains some markup via
## ANSI escape sequences. The sequences used here are usually
## ignored by terminals. But the &GAP; help system will substitute
## them by interpreted color and attribute sequences (see <Ref
## Var="TextAttr"/>) before displaying them. There is a default
## markup used for this but it can also be configured by the
## user, see <Ref Func="SetGAPDocTextTheme"/>. Furthermore, the text
## produced is in UTF-8 encoding. The encoding is also translated on
## the fly, if <C>GAPInfo.TermEncoding</C> is set to some encoding
## supported by <Ref Func="Encode"/>, e.g., <C>"ISO-8859-1"</C> or
## <C>"latin1"</C>.<P/>
##
## With the optional argument <A>width</A> a different length of the
## output text lines can be chosen. The default is 76 and all lines
## in the resulting text start with two spaces. This looks good on a
## terminal with a standard width of 80 characters and you probably
## don't want to use this argument.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
# the basic call, used recursively with a result r from GetElement
# and a string str or list l to which the output should be appended
# arg: r[, linelength] (then a list is returned, only for whole document)
# or: r, str[, linelength] (then the output is appended to string or
# list str)
InstallGlobalFunction(GAPDoc2Text, function(arg)
local r, str, linelength, name;
r := arg[1];
if Length(arg)=2 and (IsList(arg[2]) or (r.name = "WHOLEDOCUMENT" and
IsDirectory(arg[2]))) then
str := arg[2];
linelength := 76;
elif Length(arg)=2 and IsInt(arg[2]) then
linelength := arg[2];
str := "";
elif Length(arg)=3 then
str := arg[2];
linelength := arg[3];
else
str := "";
linelength := 76;
fi;
if r.name = "WHOLEDOCUMENT" then
r.linelength := linelength;
r.indent := "";
if IsDirectory(str) then
r.bibpath := str;
else
if Length(str) = 0 then
str := ".";
fi;
r.bibpath := Directory(str);
fi;
str := "";
fi;
name := r.name;
if not IsBound(GAPDoc2TextProcs.(name)) then
Info(InfoGAPDoc, 1, "#W WARNING: Don't know how to process element ", name,
" ---- ignored\n");
else
GAPDoc2TextProcs.(r.name)(r, str);
fi;
if r.name = "WHOLEDOCUMENT" then
# generate sorted list of counts from .six entries, makes LaTeX and HTML
# converter much faster on large documents
r.sixcount := List(r.six, a-> a[3]);
r.sixindex := [1..Length(r.six)];
SortParallel(r.sixcount, r.sixindex);
# put final record together and return it, also add line numbers to
# .six entries
return GAPDoc2TextProcs.PutFilesTogether(str, r.six);
fi;
return str;
end);
## to format paragraph which can be reformatted before display
GAPDoc2TextProcs.MarkAndFormat := function(str, flush, indlen, len)
local f, fnr, res, par;
# only handle non-empty strings
if flush = "auto" then
f := "both";
fnr := 0;
elif flush = "left" then
f := flush;
fnr := 1;
elif flush = "center" then
f := flush;
fnr := 2;
fi;
par := FormatParagraph(str, len - indlen, f,
[RepeatedString(" ", indlen), ""], WidthUTF8String);
if Length(par) = 0 then
return "";
fi;
res := Concatenation(TextAttr.CSI,String(fnr),";",String(indlen),"Y",
par{[indlen+1..Length(par)-1]});
res := Concatenation(par{[1..indlen]}, WrapTextAttribute(res,
GAPDoc2TextProcs.TextAttr.format), "\n");
return res;
end;
## recursion through the tree and formatting paragraphs
BindGlobal("GAPDoc2TextContent", function(r, l)
local tmp, par, cont, count, s, a;
# inline needed Alt elements
if IsList(r.content) and
ForAny(r.content, a-> IsRecord(a) and a.name = "Alt") then
tmp := r.content;
r := ShallowCopy(r);
r.content := [];
for a in tmp do
if IsRecord(a) and a.name = "Alt" then
if GAPDoc2TextProcs.AltYes(a) then
Append(r.content, a.content);
fi;
else
Add(r.content, a);
fi;
od;
fi;
# utility: append counter and formatted paragraph to l
par := function(s, name)
local sn, ss, pos;
# extra call for each part until a forced line break
pos := Position(s, '\004');
sn := "";
while pos <> 0 do
if pos = fail then
ss := s;
pos := 0;
else
ss := s{[1..pos-1]};
s := s{[pos+1..Length(s)]};
pos := Position(s, '\004');
fi;
Append(sn, GAPDoc2TextProcs.MarkAndFormat(ss, "auto",
Length(r.root.indent), r.root.linelength));
od;
if Length(sn)>0 then
GAPDoc2TextProcs.P(0, sn);
Add(l, count);
Add(l, sn);
fi;
end;
# if not containing paragraphs, then l is string to append to
if not r.name in GAPDoc2TextProcs.ParEls then
for a in r.content do
GAPDoc2Text(a, l);
od;
return;
fi;
# otherwise we have to collect text and paragraphs
cont := r.content;
count := r.count;
s := "";
for a in cont do
if a.count <> count then
par(s, a.name);
count := a.count;
s := "";
fi;
if a.name in GAPDoc2TextProcs.ParEls then
# recursively collect paragraphs
GAPDoc2Text(a, l);
else
# collect text for current paragraph
GAPDoc2Text(a, s);
fi;
od;
if Length(s)>0 then
par(s, 0);
fi;
end);
## write head and foot of Txt file.
GAPDoc2TextProcs.WHOLEDOCUMENT := function(r, par)
local i, pi, t, el, str, dat, datbt, bib, b, keys, need, labels,
tmp, pos, j, diff, text, stream, a, ansi, k, l, ind;
## add paragraph numbers to all nodes of the document
AddParagraphNumbersGapDocTree(r);
## add a link .root to the root of the document to all nodes
## (then we can collect information about indexing and so on
## there)
AddRootParseTree(r);
r.index := [];
r.toc := "";
r.labels := rec();
r.labeltexts := rec();
r.bibkeys := [];
# and the .six information for the online help index
# will contain pairs [string, [chap, sect, subsect]]
r.six := [];
## checking for processing instructions before the book starts
## example: <?Txt option1="value1" ?>
i := 1;
pi := rec();
while not r.content[i].name = "Book" do
if r.content[i].name = "XMLPI" then
t := r.content[i].content;
if Length(t) > 3 and t{[1..4]} = "Txt " then
el := GetSTag(Concatenation("<", t, ">"), 2);
for a in NamesOfComponents(el.attributes) do
pi.(a) := el.attributes.(a);
od;
fi;
fi;
i := i+1;
od;
## Now the actual work starts, we give the processing instructions found
## so far to the Book handler.
## We call the Book handler twice and produce index, bibliography, toc
## in between.
Info(InfoGAPDoc, 1, "#I First run, collecting cross references, ",
"index, toc, bib and so on . . .\n");
# with this flag we avoid unresolved references warnings in first run
GAPDoc2TextProcs.FirstRun := true;
GAPDoc2TextProcs.Book(r.content[i], "", pi);
GAPDoc2TextProcs.FirstRun := false;
# now the toc is ready
Info(InfoGAPDoc, 1, "#I Table of contents complete.\n");
r.toctext := r.toc;
# .index has entries of form [sorttext, subsorttext, numbertext,
# entrytext, count[, subtext]]
Info(InfoGAPDoc, 1, "#I Producing the index . . .\n");
SortBy(r.index, a-> [a[1],STRING_LOWER(a[2]),
List(SplitString(a[3],".-",""), Int)]);
str := "";
ind := r.index;
k := 1;
while k <= Length(ind) do
if k > 1 and ind[k][4] = ind[k-1][4] then
Append(str, " ");
else
Append(str, ind[k][4]);
fi;
if IsBound(ind[k][6]) then
if k = 1 or ind[k][4] <> ind[k-1][4] then
Append(str, ", ");
fi;
Append(str, ind[k][6]);
elif Length(ind[k][2]) > 0 then
if k = 1 or ind[k][4] <> ind[k-1][4] then
Append(str, ", ");
fi;
Append(str, ind[k][2]);
fi;
l := k;
while l <= Length(ind) and ind[l][4] = ind[k][4] and
((IsBound(ind[k][6]) and IsBound(ind[l][6])
and ind[k][6] = ind[l][6]) or
(not IsBound(ind[k][6]) and not IsBound(ind[l][6])
and ind[k][2] = ind[l][2])) do
Append(str, Concatenation(" ", ind[l][3], " "));
l := l+1;
od;
Append(str, "\n");
k := l;
od;
r.indextext := str;
if Length(r.bibkeys) > 0 then
GAPDocAddBibData(r);
Info(InfoGAPDoc, 1, "#I Writing bibliography . . .\n");
need := List(r.bibentries, a-> RecBibXMLEntry(a, "Text", r.bibstrings));
# copy the unique labels
for a in [1..Length(need)] do
need[a].key := r.biblabels[a];
od;
text := "";
ansi := rec(
Bib_author := GAPDoc2TextProcs.TextAttr.BibAuthor,
Bib_title := GAPDoc2TextProcs.TextAttr.BibTitle,
Bib_journal := GAPDoc2TextProcs.TextAttr.BibJournal,
Bib_volume := GAPDoc2TextProcs.TextAttr.BibVolume,
Bib_Label := GAPDoc2TextProcs.TextAttr.BibLabel,
Bib_reset := GAPDoc2TextProcs.TextAttr.BibReset[1]);
for a in need do
Append(text, StringBibAsText(a, ansi));
od;
r.bibtext := text;
fi;
# second run
r.six := [];
r.index := [];
Info(InfoGAPDoc, 1, "#I Second run through document . . .\n");
GAPDoc2TextProcs.Book(r.content[i], par, pi);
# adding .six entries from index
for a in r.index do
if Length(a[2]) > 0 then
Add(r.six, [Concatenation(a[4], " ", a[2]), a[3], a[5]]);
else
Add(r.six, [a[4], a[3], a[5]]);
fi;
od;
## remove the links to the root ???
## RemoveRootParseTree(r);
end;
## comments and processing instructions are in general ignored
GAPDoc2TextProcs.XMLPI := function(r, str)
return;
end;
GAPDoc2TextProcs.XMLCOMMENT := function(r, str)
return;
end;
# do nothing with Ignore
GAPDoc2TextProcs.Ignore := function(arg)
end;
# just process content ??? putting together here
GAPDoc2TextProcs.Book := function(r, par, pi)
r.root.Name := r.attributes.Name;
GAPDoc2TextContent(r, par);
end;
## Body is sectioning element
GAPDoc2TextProcs.Body := GAPDoc2TextContent;
## the title page, the most complicated looking function
GAPDoc2TextProcs.TitlePage := function(r, par)
local strn, l, s, a, aa, cont, ss, st, stmp, ind, len;
strn := "\n\n";
# the .six entry
Add(r.root.six, [GAPDocTexts.d.Titlepage,
GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"),
r.count{[1..3]}]);
# title
l := Filtered(r.content, a-> a.name = "Title");
s := "";
GAPDoc2TextContent(l[1], s);
s := FormatParagraph(WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.Heading),
r.root.linelength, "center", WidthUTF8String);
Append(strn, s);
Append(strn, "\n\n");
# subtitle
l := Filtered(r.content, a-> a.name = "Subtitle");
if Length(l)>0 then
s := "";
GAPDoc2TextContent(l[1], s);
s := FormatParagraph(WrapTextAttribute(s,GAPDoc2TextProcs.TextAttr.Heading),
r.root.linelength, "center", WidthUTF8String);
Append(strn, s);
Append(strn, "\n\n");
fi;
# version
l := Filtered(r.content, a-> a.name = "Version");
if Length(l)>0 then
s := "";
GAPDoc2TextContent(l[1], s);
while Length(s)>0 and s[Length(s)] in WHITESPACE do
Unbind(s[Length(s)]);
od;
s := FormatParagraph(s, r.root.linelength, "center", WidthUTF8String);
Append(strn, s);
Append(strn, "\n\n");
fi;
# date
l := Filtered(r.content, a-> a.name = "Date");
if Length(l)>0 then
s := "";
GAPDoc2TextContent(l[1], s);
s := FormatParagraph(s, r.root.linelength, "center", WidthUTF8String);
Append(strn, s);
Append(strn, "\n\n");
fi;
# author name(s)
l := Filtered(r.content, a-> a.name = "Author");
for a in l do
s := "";
aa := ShallowCopy(a);
aa.content := Filtered(a.content, b->
not b.name in ["Email", "Homepage", "Address"]);
GAPDoc2TextContent(aa, s);
s := FormatParagraph(s, r.root.linelength, "center", WidthUTF8String);
Append(strn, s);
Append(strn, "\n");
od;
Append(strn, "\n\n");
# short comment for front page
l := Filtered(r.content, a-> a.name = "TitleComment");
if Length(l)>0 then
# format narrower than later text
st := "";
r.root.linelength := r.root.linelength - 10;
s := r.root.indent;
r.root.indent := Concatenation(s, " ");
GAPDoc2TextContent(l[1], st);
r.root.indent := s;
r.root.linelength := r.root.linelength + 10;
Append(strn, st);
Append(strn, "\n\n");
fi;
# email and WWW-homepage of author(s), if given
l := Filtered(r.content, a-> a.name = "Author");
for a in l do
cont := List(a.content, b-> b.name);
if "Email" in cont or "Homepage" in cont then
s := "";
aa := ShallowCopy(a);
aa.content := Filtered(a.content, b-> not b.name in
["Email", "Homepage", "Address"]);
GAPDoc2TextContent(aa, s);
NormalizeWhitespace(s);
Append(strn, s);
if "Email" in cont then
Append(strn, Concatenation("\n ", GAPDocTexts.d.Email, ": "));
GAPDoc2Text(a.content[Position(cont, "Email")], strn);
fi;
if "Homepage" in cont then
Append(strn, "\n");
Append(strn, Concatenation(" ", GAPDocTexts.d.Homepage, ": "));
GAPDoc2Text(a.content[Position(cont, "Homepage")], strn);
fi;
if "Address" in cont then
Append(strn, "\n");
stmp := "";
ind := a.root.indent;
len := Length(GAPDocTexts.d.Address);
a.root.indent := Concatenation(ind, RepeatedString(' ', len+7));
GAPDoc2TextContent(a.content[Position(cont, "Address")], stmp);
a.root.indent := ind;
stmp[2]{Length(ind)+[1..len+7]} := Concatenation(" ",
GAPDocTexts.d.Address, ": ");
Append(strn, stmp);
fi;
Append(strn, "\n");
fi;
od;
# if an address is given outside the <Author> element(s)
l := Filtered(r.content, a-> a.name = "Address");
if Length(l) > 0 then
Append(strn, "\n\n");
stmp := "";
ind := r.root.indent;
len := Length(GAPDocTexts.d.Address);
r.root.indent := Concatenation(ind, RepeatedString(' ', len+2));
GAPDoc2TextContent(l[1], stmp);
r.root.indent := ind;
stmp[2]{Length(ind)+[1..len+2]} := Concatenation(GAPDocTexts.d.Address,
": ");
Append(strn, stmp);
fi;
Append(strn, "\n-------------------------------------------------------\n");
Add(par, r.count);
Add(par, strn);
# abstract, copyright page, acknowledgements, colophon
for ss in ["Abstract", "Copyright", "Acknowledgements", "Colophon" ] do
l := Filtered(r.content, a-> a.name = ss);
if Length(l)>0 then
# the .six entry
Add(r.root.six, [GAPDocTexts.d.(ss),
GAPDoc2TextProcs.SectionNumber(l[1].count, "Subsection"),
l[1].count{[1..3]}]);
Add(par, l[1].count);
Add(par, Concatenation(WrapTextAttribute(GAPDocTexts.d.(ss),
GAPDoc2TextProcs.TextAttr.Heading), "\n"));
GAPDoc2TextContent(l[1], par);
Append(par[Length(par)],
"\n-------------------------------------------------------\n");
fi;
od;
end;
## these produce text for an URL
## arg: r, str[, pre]
GAPDoc2TextProcs.Link := GAPDoc2TextContent;
GAPDoc2TextProcs.LinkText := GAPDoc2TextContent;
GAPDoc2TextProcs.URL := function(arg)
local r, str, pre, rr, txt, s;
r := arg[1];
str := arg[2];
if Length(arg)>2 then
pre := arg[3];
else
pre := "";
fi;
rr := First(r.content, a-> a.name = "LinkText");
if rr <> fail then
txt := "";
GAPDoc2TextContent(rr, txt);
rr := First(r.content, a-> a.name = "Link");
if rr = fail then
Info(InfoGAPDoc, 1, "#W missing <Link> element for text ", txt, "\n");
s := "MISSINGLINK";
else
s := "";
GAPDoc2TextContent(rr, s);
fi;
else
s := "";
GAPDoc2TextContent(r, s);
if IsBound(r.attributes.Text) then
txt := r.attributes.Text;
else
txt := s;
fi;
fi;
NormalizeWhitespace(s);
NormalizeWhitespace(txt);
pre := WrapTextAttribute(Concatenation(pre, s),
GAPDoc2TextProcs.TextAttr.URL);
if txt=s then
Append(str, pre);
else
Append(str, Concatenation(txt, " (", pre, ")"));
fi;
end;
GAPDoc2TextProcs.Homepage := GAPDoc2TextProcs.URL;
GAPDoc2TextProcs.Email := function(r, str)
# we add the 'mailto://' phrase
GAPDoc2TextProcs.URL(r, str, "mailto:");
end;
GAPDoc2TextProcs.Address := function(r, str)
# just process the text
GAPDoc2TextContent(r, str);
end;
## utility: generate a chapter or (sub)section-number string
GAPDoc2TextProcs.SectionNumber := function(count, sect)
local res;
res := "";
if IsString(count[1]) or count[1]>0 then
Append(res, String(count[1]));
else
res := "";
fi;
if sect="Chapter" or sect="Appendix" then
return res;
fi;
Add(res, '.');
if count[2]>0 then
Append(res, String(count[2]));
fi;
if sect="Section" then
return res;
fi;
if count[3]>0 then
Append(res, Concatenation("-", String(count[3])));
fi;
if sect="Par" then
Append(res, Concatenation(".", String(count[4])));
return res;
fi;
# default is SubSection or ManSection number
return res;
end;
## the sectioning commands are just translated and labels are
## generated, if given as attribute
GAPDoc2TextProcs.ChapSect := function(r, par, sect)
local num, posh, s, ind, strn, sm, sms, t;
# section number as string
num := GAPDoc2TextProcs.SectionNumber(r.count, sect);
# the heading
posh := Position(List(r.content, a-> a.name), "Heading");
if posh <> fail then
s := "";
# first the .six entry
GAPDoc2TextProcs.Heading1(r.content[posh], s);
# reset to heading markup if overwritten by inner elements
sm := s;
if PositionSublist(s{[5..Length(s)-8]}, "\033[1") <> fail then
for t in [ "K", "B", "M", "C", "F", "Func", "Math", "Emph", "Q",
"Package", "Arg" ] do
sm := SubstitutionSublist(sm, GAPDoc2TextProcs.TextAttr.(t)[2],
Concatenation(GAPDoc2TextProcs.TextAttr.(t)[2],
GAPDoc2TextProcs.TextAttr.Heading[2],"\027",
GAPDoc2TextProcs.TextAttr.Heading[1],"\027"));
od;
fi;
# in six entry we don't want the extra indent on reformatting
sms := SubstitutionSublist(sm, "\033[0;0Y", "\033[0;-2Y");
Add(r.root.six, [WrapTextAttribute(NormalizedWhitespace(sms),
GAPDoc2TextProcs.TextAttr.Heading),
num, r.count{[1..3]}]);
# label entry, if present
if IsBound(r.attributes.Label) then
r.root.labels.(r.attributes.Label) := num;
r.root.labeltexts.(r.attributes.Label) := s;
fi;
# the heading text
sm := Concatenation(num, " ", sm);
Add(par, r.count);
# here we assume that r.indent = ""
Add(par, Concatenation("\n", FormatParagraph(sm,
r.root.linelength, GAPDoc2TextProcs.TextAttr.Heading,
WidthUTF8String), "\n"));
# table of contents entry
if sect="Section" then
ind := " ";
elif sect="Subsection" then
ind := " ";
else
ind := "";
fi;
# here s without heading markup
Append(r.root.toc, FormatParagraph(Concatenation(num, " ", s),
r.root.linelength-Length(ind), "left", [ind, ""],
WidthUTF8String));
fi;
# the actual content
GAPDoc2TextContent(r, par);
end;
## this really produces the content of the heading
GAPDoc2TextProcs.Heading1 := function(r, str)
local a, where;
a := "";
GAPDoc2TextContent(r, a);
if a = "" then
if IsBound(r.root) and IsBound(r.root.inputorigins) then
where := OriginalPositionDocument(r.root.inputorigins,r.start);
where := Concatenation("(file ",where[1],
", line ",String(where[2]),")");
else
where := "";
fi;
Error("Empty <Heading> element is not supported ",where);
return;
fi;
Append(str, a[2]);
end;
## and this ignores the heading (for simpler recursion)
GAPDoc2TextProcs.Heading := function(r, str)
end;
GAPDoc2TextProcs.Chapter := function(r, par)
GAPDoc2TextProcs.ChapSect(r, par, "Chapter");
end;
GAPDoc2TextProcs.Appendix := function(r, par)
GAPDoc2TextProcs.ChapSect(r, par, "Appendix");
end;
GAPDoc2TextProcs.Section := function(r, par)
GAPDoc2TextProcs.ChapSect(r, par, "Section");
end;
GAPDoc2TextProcs.Subsection := function(r, par)
GAPDoc2TextProcs.ChapSect(r, par, "Subsection");
end;
## table of contents, just puts "TOC" in first run
GAPDoc2TextProcs.TableOfContents := function(r, par)
local s;
# the .six entry
Add(r.root.six, [GAPDocTexts.d.TableofContents,
GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"),
r.count{[1..3]}]);
Add(par, r.count);
if IsBound(r.root.toctext) then
s := WrapTextAttribute(Concatenation(GAPDocTexts.d.Contents," (",
r.root.Name, ")"), GAPDoc2TextProcs.TextAttr.Heading);
Add(par, Concatenation("\n\n", s, "\n\n", r.root.toctext,
"\n\n", GAPDoc2TextProcs.TextAttr.FillString[1],"\n"));
else
Add(par,"TOC\n-----------\n");
fi;
end;
## bibliography, just "BIB" in first run, store databases in root
GAPDoc2TextProcs.Bibliography := function(r, par)
local s;
# .six entries
s := GAPDoc2TextProcs.SectionNumber(r.count, "Chapter");
Add(r.root.six, [GAPDocTexts.d.Bibliography, s, r.count{[1..3]}]);
if GAPDocTexts.d.Bibliography <> GAPDocTexts.d.References then
Add(r.root.six, [GAPDocTexts.d.References, s, r.count{[1..3]}]);
fi;
r.root.bibdata := r.attributes.Databases;
if IsBound(r.attributes.Style) then
r.root.bibstyle := r.attributes.Style;
fi;
Add(par, r.count);
if IsBound(r.root.bibtext) then
Add(par, Concatenation("\n\n", WrapTextAttribute(GAPDocTexts.d.References,
GAPDoc2TextProcs.TextAttr.Heading),
"\n\n", r.root.bibtext,
"\n\n", GAPDoc2TextProcs.TextAttr.FillString[1], "\n"));
else
Add(par,"BIB\n-----------\n");
fi;
end;
## inside <M> element we don't want this filtering
GAPDoc2TextProcs.PCDATANOFILTER := function(r, str)
Append(str, r.content);
end;
## default is with filter ???changed???
GAPDoc2TextProcs.PCDATAFILTER := GAPDoc2TextProcs.PCDATANOFILTER;
GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATAFILTER;
## end of paragraph (end with double newline)
GAPDoc2TextProcs.P := function(r, str)
local l, i;
l := Length(str);
if l>0 and str[l] <> '\n' then
Append(str, "\n\n");
elif l>1 and str[l-1] <> '\n' then
Add(str, '\n');
else
# remove too many line breaks
i := l-2;
while i>0 and str[i] = '\n' do
Unbind(str[i+2]);
i := i-1;
od;
fi;
end;
## end of line, same as with .P, but no empty line
GAPDoc2TextProcs.Br := function(r, str)
# we use character \004 to mark forced line breaks, used in 'par' above
Add(str, '\004');
## GAPDoc2TextProcs.P(r, str);
## if Length(str) > 0 and str[Length(str)] = '\n' then
## Unbind(str[Length(str)]);
## fi;
end;
## wrapping text attributes
GAPDoc2TextProcs.WrapAppend := function(str, s, a)
Append(str, WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.(a)));
end;
GAPDoc2TextProcs.WrapAttr := function(r, str, a)
local s;
s := "";
GAPDoc2TextContent(r, s);
GAPDoc2TextProcs.WrapAppend(str, s, a);
end;
## GAP keywords
GAPDoc2TextProcs.K := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "K");
end;
## verbatim GAP code
GAPDoc2TextProcs.C := function(r, str)
local s;
s := "";
GAPDoc2TextContent(r, s);
# <A> elements are allowed inside <C>, so maybe we need to repeat the markup
# (with a char 23 such that it can be ignored in case of visible markup
# substitution)
s := SubstitutionSublist(s, GAPDoc2TextProcs.TextAttr.Arg[2],
Concatenation(GAPDoc2TextProcs.TextAttr.Arg[2],
GAPDoc2TextProcs.TextAttr.C[1],"\027"));
GAPDoc2TextProcs.WrapAppend(str, s, "C");
end;
## file names
GAPDoc2TextProcs.F := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "F");
end;
## argument names (same as Arg)
GAPDoc2TextProcs.A := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "Arg");
end;
## simple maths, here we try to substitute TeX command to something which
## looks ok in text mode
GAPDoc2TextProcs.M := function(r, str)
local s;
s := "";
GAPDoc2TextContent(r, s);
s := TextM(s);
GAPDoc2TextProcs.WrapAppend(str, s, "M");
end;
## in Txt this is shown in TeX format
GAPDoc2TextProcs.Math := function(r, str)
local s;
s := "";
GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATANOFILTER;
GAPDoc2TextContent(r, s);
GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATAFILTER;
GAPDoc2TextProcs.WrapAppend(str, s, "Math");
end;
## displayed maths (also in TeX format, but printed as paragraph enclosed
## by "\[" and "\]")
GAPDoc2TextProcs.Display := function(r, par)
local s;
s := "";
GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATANOFILTER;
GAPDoc2TextContent(r, s);
GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATAFILTER;
s := Concatenation(Filtered(s, IsString));
if IsBound(r.attributes.Mode) and r.attributes.Mode = "M" then
s := TextM(s);
fi;
s := WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.Display);
# change the indentation value in formatting escape sequence
s := SubstitutionSublist(s, "\033[0;0Y", "\033[0;6Y", "one");
s := Concatenation("\n", s, "\n\n");
Add(par, r.count);
Add(par, s);
end;
## emphazised text
GAPDoc2TextProcs.Emph := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "Emph");
end;
## quoted text
GAPDoc2TextProcs.Q := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "Q");
end;
## Package names
GAPDoc2TextProcs.Package := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "Package");
end;
## menu items
GAPDoc2TextProcs.B := function(r, str)
GAPDoc2TextProcs.WrapAttr(r, str, "B");
end;
GAPDoc2TextProcs.AddColorPromptMarkup := function(cont)
local res, pos, s;
res := [];
for s in cont do
if Length(s) > 4 and s{[1..5]} = "gap> " then
Add(res, Concatenation(WrapTextAttribute("gap>",
GAPDoc2TextProcs.TextAttr.Prompt),
" ", WrapTextAttribute(s{[6..Length(s)]},
GAPDoc2TextProcs.TextAttr.GAPInput)));
elif Length(s) > 1 and s{[1,2]} = "> " then
Add(res, Concatenation(WrapTextAttribute(">",
GAPDoc2TextProcs.TextAttr.Prompt),
" ", WrapTextAttribute(s{[3..Length(s)]},
GAPDoc2TextProcs.TextAttr.GAPInput)));
elif Length(s) > 2 and s{[1..3]} = "brk" then
pos := Position(s, ' ');
if pos <> fail then
Add(res, Concatenation(WrapTextAttribute(s{[1..pos-1]},
GAPDoc2TextProcs.TextAttr.BrkPrompt),
" ", WrapTextAttribute(s{[pos+1..Length(s)]},
GAPDoc2TextProcs.TextAttr.GAPInput)));
else
Add(res, WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.BrkPrompt));
fi;
else
Add(res, WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.GAPOutput));
fi;
od;
return res;
end;
GAPDoc2TextProcs.ExampleLike := function(r, par, label)
local str, cont, a, s, len, l1;
if Length(label) = 0 then
str := Concatenation(r.root.indent,
GAPDoc2TextProcs.TextAttr.FillString[1]);
else
str := Concatenation(r.root.indent,
GAPDoc2TextProcs.TextAttr.FillString[1],
" ", label, " ",
GAPDoc2TextProcs.TextAttr.FillString[1]);
fi;
str := WrapTextAttribute(str, GAPDoc2TextProcs.TextAttr.Example);
Add(str, '\n');
cont := "";
for a in r.content do
# here we try to avoid reformatting
if IsString(a.content) then
Append(cont, a.content);
else
s := "";
GAPDoc2Text(a, s);
Append(cont, s);
fi;
od;
cont := SplitString(cont, "\n", "");
# delete first line, if whitespace only
if Length(cont) > 0 and ForAll(cont[1], x-> x in WHITESPACE) then
cont := cont{[2..Length(cont)]};
fi;
# color prompt markup in <Example> and <Log> elements
if label in [GAPDocTexts.d.Example, GAPDocTexts.d.Log] then
cont := GAPDoc2TextProcs.AddColorPromptMarkup(cont);
fi;
cont := Concatenation(List(cont, a-> Concatenation(r.root.indent,
" ",
WrapTextAttribute(a, GAPDoc2TextProcs.TextAttr.Example),
"\n")));
Append(str, cont);
Append(str, Concatenation(r.root.indent,
WrapTextAttribute(GAPDoc2TextProcs.TextAttr.FillString[1],
GAPDoc2TextProcs.TextAttr.Example),
"\n\n"));
Add(par, r.count);
Add(par, str);
end;
## log of session and GAP code is typeset the same way as <Example>
GAPDoc2TextProcs.Example := function(r, par)
GAPDoc2TextProcs.ExampleLike(r, par, GAPDocTexts.d.Example);
end;
GAPDoc2TextProcs.Log := function(r, par)
GAPDoc2TextProcs.ExampleLike(r, par, GAPDocTexts.d.Log);
end;
GAPDoc2TextProcs.Listing := function(r, par)
if IsBound(r.attributes.Type) then
GAPDoc2TextProcs.ExampleLike(r, par, r.attributes.Type);
else
GAPDoc2TextProcs.ExampleLike(r, par, "");
fi;
end;
## Verb is without any formatting
GAPDoc2TextProcs.Verb := function(r, par)
local cont, s, pos, a;
cont := "";
for a in r.content do
# here we try to avoid reformatting
if IsString(a.content) then
Append(cont, a.content);
else
s := "";
GAPDoc2Text(a, s);
Append(cont, s);
fi;
od;
# delete first line if it contains only whitespace
pos := Position(cont, '\n');
if pos <> fail and ForAll(cont{[1..pos]}, x-> x in WHITESPACE) then
cont := cont{[pos+1..Length(cont)]};
fi;
# adjust trailing newlines
GAPDoc2TextProcs.P(0, cont);
Append(par, [r.count, cont]);
end;
## explicit labels
GAPDoc2TextProcs.Label := function(r, str)
r.root.labels.(r.attributes.Name) :=
GAPDoc2TextProcs.SectionNumber(r.count, "Subsection");
end;
## citations
GAPDoc2TextProcs.Cite := function(r, str)
local key, pos;
key := r.attributes.Key;
pos := Position(r.root.bibkeys, key);
if pos = fail then
Add(r.root.bibkeys, key);
Append(str, Concatenation("[?", key, "?]"));
elif not IsBound(r.root.biblabels) then
Append(str, Concatenation("[?", key, "?]"));
else
Append(str, Concatenation("[", r.root.biblabels[pos]));
if IsBound(r.attributes.Where) then
Append(str, ", ");
Append(str, r.attributes.Where);
fi;
Add(str, ']');
fi;
end;
## explicit index entries
GAPDoc2TextProcs.Subkey := GAPDoc2TextContent;
GAPDoc2TextProcs.Index := function(r, str)
local s, sub, entry, a;
s := "";
sub := "";
for a in r.content do
if a.name = "Subkey" then
GAPDoc2Text(a, sub);
else
GAPDoc2Text(a, s);
fi;
od;
NormalizeWhitespace(s);
NormalizeWhitespace(sub);
if IsBound(r.attributes.Key) then
entry := [STRING_LOWER(r.attributes.Key)];
else
entry := [STRING_LOWER(StripEscapeSequences(s))];
fi;
if IsBound(r.attributes.Subkey) then
Add(entry, r.attributes.Subkey);
else
Add(entry, STRING_LOWER(StripEscapeSequences(sub)));
fi;
Add(entry, GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"));
Add(entry, s);
Add(entry, r.count{[1..3]});
if Length(sub) > 0 then
Add(entry, sub);
fi;
Add(r.root.index, entry);
end;
## helper to add markup to the args
GAPDoc2TextProcs.WrapArgs := function(argstr)
local res, noletter, c;
res := "";
noletter := true;
for c in argstr do
if noletter then
if not c in ", []" then
noletter := false;
Append(res, GAPDoc2TextProcs.TextAttr.Arg[1]);
fi;
elif c in ", []" then
noletter := true;
Append(res, GAPDoc2TextProcs.TextAttr.Arg[2]);
fi;
Add(res, c);
od;
if not noletter then
Append(res, GAPDoc2TextProcs.TextAttr.Arg[2]);
fi;
return res;
end;
## this produces an implicit index entry and a label entry
GAPDoc2TextProcs.LikeFunc := function(r, par, typ)
local str, s, name, lab, comma, i, entry;
# for very long lines we allow left flushed paragraph formatting
s := Concatenation(GAPDoc2TextProcs.TextAttr.format[1], "\033[1;0Y",
GAPDoc2TextProcs.TextAttr.DefLineMarker[1],
WrapTextAttribute(r.attributes.Name, GAPDoc2TextProcs.TextAttr.Func));
if IsBound(r.attributes.Arg) then
Append(s, "( ");
Append(s, GAPDoc2TextProcs.WrapArgs(NormalizedArgList(r.attributes.Arg)));
Append(s, " ) ");
fi;
# label (if not given, the default is the Name)
if IsBound(r.attributes.Label) then
lab := r.attributes.Label;
comma := ", ";
else
lab := "";
comma := "";
fi;
GAPDoc2TextProcs.Label(rec(count := r.count, attributes := rec(Name
:= Concatenation(r.attributes.Name, comma, lab)), root := r.root), par);
# index entry
name := r.attributes.Name;
entry := [STRING_LOWER(name), "",
GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"),
WrapTextAttribute(name, GAPDoc2TextProcs.TextAttr.Func),
r.count{[1..3]}];
if Length(lab) > 0 then
entry[2] := STRING_LOWER(StripEscapeSequences(lab));
Add(entry, lab);
fi;
Add(r.root.index, entry);
# some hint about the type of the variable
Append(s, GAPDoc2TextProcs.TextAttr.FillString[1]);
Append(s, Concatenation(" ",typ,GAPDoc2TextProcs.TextAttr.format[2],"\n"));
Add(par, r.count);
Add(par, s);
end;
GAPDoc2TextProcs.Func := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Func);
end;
GAPDoc2TextProcs.Oper := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Oper);
end;
GAPDoc2TextProcs.Constr := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Constr);
end;
GAPDoc2TextProcs.Meth := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Meth);
end;
GAPDoc2TextProcs.Filt := function(r, str)
# r.attributes.Type could be "representation", "category", ...
if IsBound(r.attributes.Type) then
GAPDoc2TextProcs.LikeFunc(r, str, r.attributes.Type);
else
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Filt);
fi;
end;
GAPDoc2TextProcs.Prop := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Prop);
end;
GAPDoc2TextProcs.Attr := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Attr);
end;
GAPDoc2TextProcs.Var := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Var);
end;
GAPDoc2TextProcs.Fam := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Fam);
end;
GAPDoc2TextProcs.InfoClass := function(r, str)
GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.InfoClass);
end;
## using the HelpData(.., .., "ref") interface
GAPDoc2TextProcs.ResolveExternalRef := function(bookname, label, nr)
local info, match, res;
info := HELP_BOOK_INFO(bookname);
if info = fail then
return fail;
fi;
match := Concatenation(HELP_GET_MATCHES(info, SIMPLE_STRING(label), true));
if Length(match) < nr then
return fail;
fi;
res := GetHelpDataRef(info, match[nr][2]);
res[1] := SubstitutionSublist(res[1], " (not loaded): ", ": ", "one");
return res;
end;
GAPDoc2TextProcs.Ref := function(r, str)
local funclike, int, txt, ref, lab, sectlike;
# function like cases
funclike := [ "Func", "Oper", "Constr", "Meth", "Filt", "Prop", "Attr",
"Var", "Fam", "InfoClass" ];
int := Intersection(funclike, NamesOfComponents(r.attributes));
if Length(int)>0 then
txt := r.attributes.(int[1]);
if IsBound(r.attributes.Label) then
lab := Concatenation(txt, ", ", r.attributes.Label);
else
lab := txt;
fi;
if IsBound(r.attributes.BookName) then
ref := GAPDoc2TextProcs.ResolveExternalRef(r.attributes.BookName, lab, 1);
if ref = fail then
if GAPDoc2TextProcs.FirstRun <> true then
Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
r.attributes, "\n");
fi;
ref := Concatenation(lab, "???");
else
# the search text for online help including book name
ref := ref[1];
fi;
else
if IsBound(r.root.labels.(lab)) then
ref := r.root.labels.(lab);
else
if GAPDoc2TextProcs.FirstRun <> true then
Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
r.attributes, "\n");
fi;
ref := Concatenation("???", lab, "???");
fi;
fi;
Append(str, WrapTextAttribute(txt, GAPDoc2TextProcs.TextAttr.Func));
# add reference by subsection number or text if external,
# but only if it does not point to current subsection
if GAPDoc2TextProcs.SectionNumber(r.count, "Subsection") <> ref then
Append(str, Concatenation(" (", WrapTextAttribute(ref,
GAPDoc2TextProcs.TextAttr.Ref), ")"));
fi;
return;
fi;
# section like cases
sectlike := ["Chap", "Sect", "Subsect", "Appendix"];
int := Intersection(sectlike, NamesOfComponents(r.attributes));
if Length(int)>0 then
txt := r.attributes.(int[1]);
if IsBound(r.attributes.Label) then
lab := Concatenation(txt, r.attributes.Label);
else
lab := txt;
fi;
if IsBound(r.attributes.BookName) then
ref := GAPDoc2TextProcs.ResolveExternalRef(r.attributes.BookName, lab, 1);
if ref = fail then
if GAPDoc2TextProcs.FirstRun <> true then
Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
r.attributes, "\n");
fi;
ref := Concatenation(lab, "???");
else
# the search text for online help including book name
ref := Concatenation("'", StripBeginEnd(ref[1], " "), "'");
fi;
else
# with sectioning references Label must be given
lab := r.attributes.(int[1]);
# default is printing section number, but we allow a Style="Text"
# attribute
if IsBound(r.attributes.Style) and r.attributes.Style = "Text" and
IsBound(r.root.labeltexts.(lab)) then
ref := Concatenation("'", StripBeginEnd(
r.root.labeltexts.(lab), WHITESPACE), "'");
elif IsBound(r.root.labels.(lab)) then
ref := r.root.labels.(lab);
else
if GAPDoc2TextProcs.FirstRun <> true then
Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
r.attributes, "\n");
fi;
ref := Concatenation("???", lab, "???");
fi;
fi;
Append(str, WrapTextAttribute(ref, GAPDoc2TextProcs.TextAttr.Ref));
return;
fi;
# neutral reference to a label
lab := r.attributes.Label;
if IsBound(r.attributes.BookName) then
ref := GAPDoc2TextProcs.ResolveExternalRef(r.attributes.BookName, lab, 1);
if ref = fail then
ref := Concatenation(lab, "???");
else
# the search text for online help including book name
ref := ref[1];
fi;
else
if IsBound(r.root.labels.(lab)) then
ref := r.root.labels.(lab);
else
ref := Concatenation("???", lab, "???");
fi;
fi;
Append(str, WrapTextAttribute(ref, GAPDoc2TextProcs.TextAttr.Ref));
return;
end;
GAPDoc2TextProcs.Description := function(r, par)
local l, tmp;
l := "";
GAPDoc2TextContent(r, l);
# Add an empty line in front if not yet there
if Length(par) > 0 and Length(par[Length(par)]) > 1 then
tmp := par[Length(par)];
else
tmp := "";
fi;
if tmp[Length(tmp)-1] <> '\n' then
Add(tmp, '\n');
fi;
Append(par, l);
end;
GAPDoc2TextProcs.Returns := function(r, par)
local l, ind, lr, pos;
l := "";
ind := r.root.indent;
r.root.indent := Concatenation(ind, " ");
GAPDoc2TextContent(r, l);
if Length(l) > 0 then
lr := Length(GAPDocTexts.d.Returns)+1;
pos := PositionSublist(l[2], RepeatedString(" ",lr), Length(ind));
l[2] := Concatenation(l[2]{[1..pos-1]},
WrapTextAttribute(Concatenation(GAPDocTexts.d.Returns,":"),
GAPDoc2TextProcs.TextAttr.Returns),
l[2]{[pos+lr..Length(l[2])]});
Append(par, l);
fi;
r.root.indent := ind;
end;
GAPDoc2TextProcs.ManSection := function(r, par)
local funclike, i, num, s, strn;
# if there is a Heading then handle as subsection
if ForAny(r.content, a-> IsRecord(a) and a.name = "Heading") then
GAPDoc2TextProcs.ChapSect(r, par, "Subsection");
return;
fi;
strn := "";
# function like elements
funclike := [ "Func", "Oper", "Constr", "Meth", "Filt", "Prop", "Attr",
"Var", "Fam", "InfoClass" ];
# heading comes from name of first function like element
i := 1;
while not r.content[i].name in funclike do
i := i+1;
od;
num := GAPDoc2TextProcs.SectionNumber(r.count, "Subsection");
s := Concatenation(num, " ", r.content[i].attributes.Name);
Add(par, r.count);
Add(par, Concatenation(WrapTextAttribute(s,
GAPDoc2TextProcs.TextAttr.Heading), "\n\n"));
# append to TOC as subsection
Append(r.root.toc, Concatenation(" ", s, "\n"));
# label entry, if present
if IsBound(r.attributes.Label) then
r.root.labels.(r.attributes.Label) := num;
r.root.labeltexts.(r.attributes.Label) := s;
fi;
GAPDoc2TextContent(r, par);
end;
GAPDoc2TextProcs.Mark := function(r, str)
local s;
s := "";
GAPDoc2TextProcs.WrapAttr(r, s, "Mark");
# allow for <C> and <A> elements in <Mark>
s := SubstitutionSublist(s, GAPDoc2TextProcs.TextAttr.Arg[2],
Concatenation(GAPDoc2TextProcs.TextAttr.Arg[2],
GAPDoc2TextProcs.TextAttr.Mark[1],"\027"));
s := SubstitutionSublist(s, GAPDoc2TextProcs.TextAttr.C[2],
Concatenation(GAPDoc2TextProcs.TextAttr.C[2],
GAPDoc2TextProcs.TextAttr.Mark[1],"\027"));
Append(str, r.root.indent);
Append(str, NormalizedWhitespace(s));
Append(str, "\n");
end;
GAPDoc2TextProcs.Item := function(r, str)
local s;
# s := "";
s := r.root.indent;
r.root.indent := Concatenation(s, " ");
GAPDoc2TextContent(r, str);
r.root.indent := s;
# s:= FormatParagraph(s, r.root.linelength-6, "both", [" ", ""]);
# Append(str, s);
end;
# must do the complete formatting
GAPDoc2TextProcs.List := function(r, par)
local s, ss, pos, a, i, start;
if "Mark" in List(r.content, a-> a.name) then
for a in r.content do
if a.name = "Mark" then
s := "";
GAPDoc2TextProcs.Mark(a, s);
Append(par, [a.count, s]);
elif a.name = "Item" then
GAPDoc2TextProcs.Item(a, par);
fi;
od;
else
for a in Filtered(r.content, a-> a.name = "Item") do
ss := "";
GAPDoc2TextProcs.Item(a, ss);
if ss <> "" then # ignore empty <Item>
# insert bullet
start := ss[2]{[1..Length(r.root.indent)]};
ss[2] := ss[2]{[Length(r.root.indent)+1..Length(ss[2])]};
for i in [1..2] do
Remove(ss[2], Position(ss[2],' '));
od;
ss[2] := Concatenation(start,
GAPDoc2TextProcs.TextAttr.ListBullet[1], ss[2]);
Append(par, ss);
fi;
od;
fi;
end;
GAPDoc2TextProcs.Enum := function(r, par)
local i, ss, num, a, j, start;
i := 1;
for a in Filtered(r.content, a-> a.name = "Item") do
ss := "";
GAPDoc2TextProcs.Item(a, ss);
# merge in the counter
start := ss[2]{[1..Length(r.root.indent)]};
ss[2] := ss[2]{[Length(r.root.indent)+1..Length(ss[2])]};
for j in [1..Length(String(i))+2] do
Remove(ss[2], Position(ss[2],' '));
od;
num := WrapTextAttribute(String(i), GAPDoc2TextProcs.TextAttr.EnumMarks);
ss[2] := Concatenation(start, num, ss[2]);
Append(par, ss);
i := i+1;
od;
end;
GAPDoc2TextProcs.TheIndex := function(r, par)
local s;
# .six entry
s := GAPDoc2TextProcs.SectionNumber(r.count, "Chapter");
Add(r.root.six, [GAPDocTexts.d.Index, s, r.count{[1..3]}]);
# the text, if available
Add(par, r.count);
if IsBound(r.root.indextext) then
Add(par, Concatenation("\n\n",
WrapTextAttribute(GAPDocTexts.d.Index,
GAPDoc2TextProcs.TextAttr.Heading),
"\n\n", r.root.indextext,
"\n\n-------------------------------------------------------\n"));
else
Add(par,"INDEX\n-----------\n");
fi;
end;
GAPDoc2TextProcs.AltYes := function(r)
if (not IsBound(r.attributes.Only) and not IsBound(r.attributes.Not)) or
(IsBound(r.attributes.Only) and
"Text" in SplitString(r.attributes.Only, "", " ,")) or
(IsBound(r.attributes.Not) and
not "Text" in SplitString(r.attributes.Not, "", " ,")) then
return true;
else
return false;
fi;
end;
GAPDoc2TextProcs.Alt := function(r, str)
if GAPDoc2TextProcs.AltYes(r) then
GAPDoc2TextContent(r, str);
fi;
end;
# copy a few entries with two element names
GAPDoc2TextProcs.E := GAPDoc2TextProcs.Emph;
GAPDoc2TextProcs.Keyword := GAPDoc2TextProcs.K;
GAPDoc2TextProcs.Code := GAPDoc2TextProcs.C;
GAPDoc2TextProcs.File := GAPDoc2TextProcs.F;
GAPDoc2TextProcs.Button := GAPDoc2TextProcs.B;
GAPDoc2TextProcs.Arg := GAPDoc2TextProcs.A;
GAPDoc2TextProcs.Quoted := GAPDoc2TextProcs.Q;
GAPDoc2TextProcs.Par := GAPDoc2TextProcs.P;
# like PCDATA
GAPDoc2TextProcs.EntityValue := GAPDoc2TextProcs.PCDATA;
GAPDoc2TextProcs.Table := function(r, str)
local a, align, bc, t, z, b, l, s, pos, lens, m, d, ind, hline, cap, i, j;
if not GAPDoc2TextProcs.AltYes(r) then
return;
fi;
# head part of table and tabular
if IsBound(r.attributes.Label) then
r.root.labels.(r.attributes.Label) :=
GAPDoc2TextProcs.SectionNumber(r.count, "Subsection");
fi;
# add spaces as separators of colums if no "|" is given
# first, add a dummy character at the end of the input to
# simplify handling of the last position
a := Concatenation(r.attributes.Align, " ");
align := "";
for i in [1..Length(a)-1] do
if a[i] in "crl" then
Add(align, a[i]);
if a[i+1] <> '|' then
Add(align, ' ');
fi;
elif a[i] = '|' then
Add(align, '|');
fi;
od;
# make all odd positions separator descriptions
if not align[1] in " |" then
align := Concatenation(" ", align);
fi;
# box characters
bc := List([1..11], i-> BOXCHARS{[3*i-2..3*i]});
# collect entries
t := [];
# the rows of the table
for a in r.content do
if a.name = "Row" then
z := [];
b := Filtered(a.content, x-> x.name = "Item");
for i in [1..Length(align)] do
if i mod 2 = 1 then
if align[i] = '|' then
Add(z, Concatenation(" ", bc[2], " "));
else
Add(z, Concatenation(" ", align{[i]}, " "));
fi;
elif IsBound(b[i/2]) then
l := "";
GAPDoc2TextProcs.Item(b[i/2], l);
s := Concatenation(l{[2,4..Length(l)]});
NormalizeWhitespace(s);
# do not reformat paragraphs later
pos := PositionSublist(s, GAPDoc2TextProcs.TextAttr.format[1]);
if pos = 1 then
s := s{[Position(s, 'Y')+1..PositionSublist(s,
GAPDoc2TextProcs.TextAttr.format[2])-1]};
fi;
Add(z, s);
else
Add(z, "");
fi;
od;
Add(t, z);
elif a.name = "HorLine" then
Add(t, List(align, x-> ""));
fi;
od;
# equalize width of entries in columns
lens := [];
for i in [2,4..2*QuoInt(Length(align), 2)] do
a := List(t, b-> WidthUTF8String(StripEscapeSequences(
SubstituteEscapeSequences(b[i], GAPDocTextTheme))));
m := Maximum(a);
lens[i] := m;
z := "";
for b in [1..m] do
Add(z, ' ');
od;
if align[i] = 'r' then
for j in [1..Length(t)] do
t[j][i] := Concatenation(z{[1..m-a[j]]}, t[j][i]);
od;
elif align[i] = 'l' then
for j in [1..Length(t)] do
t[j][i] := Concatenation(t[j][i], z{[1..m-a[j]]});
od;
else
for j in [1..Length(t)] do
d := m - a[j];
t[j][i] := Concatenation(z{[1..QuoInt(d, 2)]}, t[j][i],
z{[1..d - QuoInt(d, 2)]});
od;
fi;
od;
# put lines together
for i in [1..Length(t)] do
if Length(t[i][1])=0 then
t[i] := ["-"];
fi;
od;
t := List(t, Concatenation);
hline := function(t,l,m,r)
local z, i, j;
# Process first column
z := " ";
if align[1] = '|' then
Append(z, l);
Append(z, t);
else
Append(z, " ");
fi;
# Process all columns excluding the first and last one
for i in [2..Length(align)-1] do
if i mod 2 = 0 then
for j in [1..lens[i]] do
Append(z, t);
od;
elif align[i] = '|' then
Append(z, t);
Append(z, m);
Append(z, t);
else
Append(z, " ");
fi;
od;
# Process last column
if align[Length(align)] = '|' then
Append(z, t);
Append(z, r);
else
Append(z, " ");
fi;
Add(z, '\n');
return z;
end;
for i in [1..Length(t)] do
if t[i][1] = '-' then
if i = 1 then
t[i] := hline(bc[1],bc[3],bc[4],bc[5]);
elif i = Length(t) then
t[i] := hline(bc[1],bc[9],bc[10],bc[11]);
else
t[i] := hline(bc[1],bc[6],bc[7],bc[8]);
fi;
else
t[i] := Concatenation(" ", t[i], "\n");
fi;
od;
t := Concatenation(t);
Add(t, '\n');
# the caption, if given
cap := Filtered(r.content, a-> a.name = "Caption");
if Length(cap) > 0 then
s := "";
GAPDoc2TextProcs.Caption1(cap[1], s);
Append(t, s);
Append(t, "\n\n");
fi;
Add(str, r.count);
Add(str, t);
end;
# do nothing, we call .Caption1 directly in .Table
GAPDoc2TextProcs.Caption := function(r, str)
return;
end;
# here the caption for a table text is produced
GAPDoc2TextProcs.Caption1 := function(r, str)
local s;
s := Concatenation(GAPDocTexts.d.Table,":");
s := WrapTextAttribute(s,GAPDoc2TextProcs.TextAttr.Heading);
Append(s, " ");
GAPDoc2TextContent(r, s);
Append(str, FormatParagraph(s, r.root.linelength - 10,
"both", [" ", ""], WidthUTF8String));
end;
##
## <#GAPDoc Label="GAPDoc2TextPrintTextFiles">
## <ManSection >
## <Func Arg="t[, path]" Name="GAPDoc2TextPrintTextFiles" />
## <Returns>nothing</Returns>
## <Description>
## The first argument must be a result returned by <Ref
## Func="GAPDoc2Text"/>. The second argument is a path for the files
## to write, it can be given as string or directory object. The text
## of each chapter is written into a separate file with name
## <F>chap0.txt</F>, <F>chap1.txt</F>, ..., <F>chapBib.txt</F>, and
## <F>chapInd.txt</F>.<P/>
##
## If you want to make your document accessible via the &GAP; online
## help you must put at least these files for the text version
## into a directory, together with the file <F>manual.six</F>,
## see <Ref Func="PrintSixFile" />. Then specify the path to the
## <F>manual.six</F> file in the packages <F>PackageInfo.g</F> file,
## see <Ref Sect="The PackageInfo.g File" BookName="reference"/>.
## <P/>
##
## Optionally you can add the <C>dvi</C>- and <C>pdf</C>-versions of
## the document which are produced with <Ref Func="GAPDoc2LaTeX"
## /> to this directory. The files must have the names
## <F>manual.dvi</F> and <F>manual.pdf</F>, respectively. Also
## you can add the files of the HTML version produced with
## <Ref Func="GAPDoc2HTML" /> to this directory, see <Ref
## Func="GAPDoc2HTMLPrintHTMLFiles" />. The handler functions in
## &GAP; for this help format detect automatically which of the
## optional formats of a book are actually available.
##
## </Description>
## </ManSection>
## <#/GAPDoc>
##
# arg: t (result of GAPDoc2Text)[, directory]
InstallGlobalFunction(GAPDoc2TextPrintTextFiles, function(arg)
local t, dir, a;
t := arg[1];
if Length(arg)>1 then
dir := arg[2];
if IsString(dir) then
dir := Directory(dir);
fi;
else
dir := Directory("");
fi;
for a in NamesOfComponents(t) do
FileString(Filename(dir,Concatenation("chap",a,".txt")), t.(a).text);
od;
end);