############################################################################# ## #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);
## 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)=3then
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) = 0then
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) = 0then
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 <> 0do 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)>0then
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)>0then
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]) > 0then 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) > 0then
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) > 5then
Add(r.six, [Concatenation(a[4], " ", a[6]), a[3], a[5]]); elif Length(a[2]) > 0then # subkey given as attribute, no markup
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)>0then
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)>0then
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)>0then
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)>0then # 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) > 0then
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;
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]>=0then
Append(res, String(count[1])); else
res := ""; fi; if sect="Chapter" or sect="Appendix"then
return res; fi;
Add(res, '.'); if count[2]>=0then
Append(res, String(count[2])); fi; if sect="Section"then
return res; fi; if count[3]>0then
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, lb, 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
lb := NormalizedWhitespace(r.attributes.Label);
r.root.labels.(lb) := num;
r.root.labeltexts.(lb) := 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;
## 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;
## 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;
## 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;
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) = 0then
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;
## 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) > 0then
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, " ) "); else
Append(s, " "); fi; # label (if not given, the default is the Name) if IsBound(r.attributes.Label) then
lab := NormalizedWhitespace(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) > 0then
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;
## 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;
# function like cases
funclike := [ "Func", "Oper", "Constr", "Meth", "Filt", "Prop", "Attr", "Var", "Fam", "InfoClass" ];
int := Intersection(funclike, NamesOfComponents(r.attributes)); if Length(int)>0then
txt := r.attributes.(int[1]); if IsBound(r.attributes.Label) then
lab := Concatenation(txt, ", ", r.attributes.Label); else
lab := txt; fi;
lab := NormalizedWhitespace(lab); 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)>0then
txt := r.attributes.(int[1]); if IsBound(r.attributes.Label) then
lab := Concatenation(txt, r.attributes.Label); else
lab := txt; fi;
lab := NormalizedWhitespace(lab); 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 := NormalizedWhitespace(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 := NormalizedWhitespace(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)]) > 1then
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) > 0then
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, lb, 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
lb := NormalizedWhitespace(r.attributes.Label);
r.root.labels.(lb) := num;
r.root.labeltexts.(lb) := 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;
# 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.(NormalizedWhitespace(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 = 1then 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 = 1then
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])=0then
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 = 0then 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 = 1then
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) > 0then
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)>1then
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);
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.39Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.