fpc/utils/fpdoc/dw_latex.pp
Michaël Van Canneyt a4e8a15c70 * PChar -> PAnsiChar
2023-07-24 14:55:41 +02:00

842 lines
19 KiB
ObjectPascal

{
FPDoc - Free Pascal Documentation Tool
Copyright (C) 2000 - 2003 by
Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org
* LaTeX output generator
See the file COPYING, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
{$mode objfpc}
{$H+}
unit dw_LaTeX;
{$WARN 5024 off : Parameter "$1" not used}
interface
uses DOM, dGlobals, PasTree;
const
LateXHighLight : Boolean = False;
MaxVerbatimLength : Integer = 65;
TexExtension : String = '.tex';
Procedure CreateLaTeXDocForPackage(APackage: TPasPackage; AEngine: TFPDocEngine);
implementation
uses fpdocstrs, SysUtils, Classes, dwLinear, dwriter;
Type
{ TLaTeXWriter }
TLaTeXWriter = class(TLinearWriter)
protected
FLink: String;
FImageDir: String;
FTableCount : Integer;
FInVerbatim : Boolean;
Inlist,
TableRowStartFlag,
TableCaptionWritten: Boolean;
// Linear documentation methods overrides;
procedure WriteLabel(Const S : String); override;
procedure WriteIndex(Const S : String); override;
Procedure WriteExampleFile(FN : String); override;
Procedure StartProcedure; override;
Procedure EndProcedure; override;
Procedure StartProperty; override;
Procedure EndProperty; override;
Procedure StartSynopsis; override;
Procedure StartDeclaration; override;
Procedure StartVisibility; override;
Procedure StartDescription; override;
Procedure StartAccess; override;
Procedure StartErrors; override;
Procedure StartVersion; override;
Procedure StartSeealso; override;
Procedure EndSeealso; override;
procedure StartUnitOverview(AModuleName,AModuleLabel : String);override;
procedure WriteUnitEntry(UnitRef : TPasType); override;
Procedure EndUnitOverview; override;
function GetLabel(AElement: TPasElement): String; override;
procedure StartListing(Frames: Boolean; const name: String); override;
procedure EndListing; override;
Function EscapeText(S : AnsiString) : AnsiString; override;
Function StripText(S : String) : String; override;overload;
procedure WriteCommentLine; override;
procedure WriteComment(Comment : String);override;
procedure StartSection(SectionName : String);override;
procedure StartSubSection(SubSectionName : String);override;
procedure StartSubSubSection(SubSubSectionName : String);override;
procedure StartChapter(ChapterName : String); override;
procedure StartOverview(Const What : String; WithAccess : Boolean); override;
procedure EndOverview; override;
procedure WriteOverviewMember(const ALabel,AName,Access,ADescr : String); override;
procedure WriteOverviewMember(const ALabel,AName,ADescr : String); override;
// Description node conversion
procedure DescrBeginBold; override;
procedure DescrEndBold; override;
procedure DescrBeginItalic; override;
procedure DescrEndItalic; override;
procedure DescrBeginEmph; override;
procedure DescrEndEmph; override;
procedure DescrBeginUnderline; override;
procedure DescrEndUnderline; override;
procedure DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString); override;
procedure DescrWriteFileEl(const AText: DOMString); override;
procedure DescrWriteKeywordEl(const AText: DOMString); override;
procedure DescrWriteVarEl(const AText: DOMString); override;
procedure DescrBeginLink(const AId: DOMString); override;
procedure DescrEndLink; override;
procedure DescrBeginURL(const AURL: DOMString); override; // Provides a default implementation
procedure DescrEndURL; override;
procedure DescrWriteLinebreak; override;
procedure DescrBeginParagraph; override;
procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); override;
procedure DescrWriteCodeLine(const ALine: String); override;
procedure DescrEndCode; override;
procedure DescrEndParagraph; override;
procedure DescrBeginOrderedList; override;
procedure DescrEndOrderedList; override;
procedure DescrBeginUnorderedList; override;
procedure DescrEndUnorderedList; override;
procedure DescrBeginDefinitionList; override;
procedure DescrEndDefinitionList; override;
procedure DescrBeginListItem; override;
procedure DescrEndListItem; override;
procedure DescrBeginDefinitionTerm; override;
procedure DescrEndDefinitionTerm; override;
procedure DescrBeginDefinitionEntry; override;
procedure DescrEndDefinitionEntry; override;
procedure DescrBeginSectionTitle; override;
procedure DescrBeginSectionBody; override;
procedure DescrEndSection; override;
procedure DescrBeginRemark; override;
procedure DescrEndRemark; override;
procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); override;
procedure DescrEndTable; override;
procedure DescrBeginTableCaption; override;
procedure DescrEndTableCaption; override;
procedure DescrBeginTableHeadRow; override;
procedure DescrEndTableHeadRow; override;
procedure DescrBeginTableRow; override;
procedure DescrEndTableRow; override;
procedure DescrBeginTableCell; override;
procedure DescrEndTableCell; override;
// TFPDocWriter class methods
Property ImageDir : String Read FImageDir Write FImageDir;
public
Function SplitLine (ALine : String): String; virtual;
Function InterPretOption(Const Cmd,Arg : String) : boolean; override;
Class Function FileNameExtension : String; override;
class procedure Usage(List: TStrings); override;
end;
function TLaTeXWriter.GetLabel(AElement: TPasElement): String;
var
i: Integer;
begin
if AElement.ClassType = TPasUnresolvedTypeRef then
Result := Engine.ResolveLink(Module, AElement.Name)
else
begin
Result := AElement.PathName;
Result := LowerCase(Copy(Result, 2, Length(Result) - 1));
end;
for i := 1 to Length(Result) do
if Result[i] = '.' then
Result[i] := ':';
end;
Function TLaTeXWriter.SplitLine (ALine : String): String;
Function FindLastSplit(S : String) : Integer;
Const
NonSplit = ['a'..'z','A'..'Z','0'..'9','_'];
Var
L,I : integer;
C : PChar;
InString : Boolean;
begin
Result:=0;
L:=Length(S);
if (L>MaxVerbatimLength) then
begin
InString:=False;
Result:=0;
I:=1;
C:=@S[1];
While (I<=L) and (Result<=MaxVerbatimLength) do
begin
If C^='''' then
InString:=Not Instring
else if Not InString then
begin
if Not (C^ in NonSplit) then
Result:=I;
end;
Inc(I);
Inc(C);
end;
end;
If (Result=0) or (Result=1) then
Result:=L+1;
end;
Var
SP : Integer;
L : String;
begin
Result:='';
While (Aline<>'') do
begin
SP:=FindLastSplit(Aline);
L:=Copy(ALine,1,SP-1);
Delete(ALine,1,SP-1);
If (Result<>'') then
Result:=Result+sLineBreak+' ';
Result:=Result+Trim(L);
end;
end;
function TLaTeXWriter.EscapeText(S: AnsiString): AnsiString;
var
i: Integer;
begin
if FInVerBatim=True then
begin
if (MaxVerbatimLength=0) or (length(S)<=MaxVerbatimLength) then
Result:=S
else
Result:=SplitLine(S);
end
else
begin
SetLength(Result, 0);
for i := 1 to Length(S) do
case S[i] of
'&','{','}','#','_','$','%': // Escape these characters
Result := Result + '\' + S[i];
'~','^':
Result := Result + '\'+S[i]+' ';
'\':
Result:=Result+'$\backslash$'
else
Result := Result + S[i];
end;
end;
end;
function TLaTeXWriter.StripText(S: String): String;
var
I: Integer;
begin
Result:='';
for i := 1 to Length(S) do
If not (S[i] in ['&','{','}','#','_','$','%','''','~','^', '\']) then
Result := Result + S[i]
else
Result:=result+'!'
end;
procedure TLaTeXWriter.DescrBeginBold;
begin
Write('\textbf{');
end;
procedure TLaTeXWriter.DescrEndBold;
begin
Write('}');
end;
procedure TLaTeXWriter.DescrBeginItalic;
begin
Write('\textit{');
end;
procedure TLaTeXWriter.DescrEndItalic;
begin
Write('}');
end;
procedure TLaTeXWriter.DescrBeginEmph;
begin
Write('\emph{');
end;
procedure TLaTeXWriter.DescrEndEmph;
begin
Write('}');
end;
procedure TLaTeXWriter.DescrBeginUnderline;
begin
Write('\underline{');
end;
procedure TLaTeXWriter.DescrEndUnderline;
begin
Write('}');
end;
procedure TLaTeXWriter.DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString);
Var
FN : String;
L : Integer;
S : String;
begin
Writeln('\begin{figure}[ht]%');
Writeln('\begin{center}');
If (ACaption<>ACaption) then
begin
S:=EscapeText(ACaption);
Writeln(Format('\caption{%s}',[S]));
end;
If (ALinkName<>'') then
WriteLabel('fig:'+Utf8Encode(ALinkName));
FN:=ImageDir;
L:=Length(FN);
If (L>0) and (FN[l]<>'/') then
FN:=FN+'/';
FN:=FN+Utf8Encode(AFileName);
Writeln('\epsfig{file='+FN+'}');
Writeln('\end{center}');
Writeln('\end{figure}');
end;
procedure TLaTeXWriter.DescrWriteFileEl(const AText: DOMString);
begin
Write('\file{');
DescrWriteText(AText);
Write('}');
end;
procedure TLaTeXWriter.DescrWriteKeywordEl(const AText: DOMString);
begin
Write('\textbf{\\ttfamily ');
DescrWriteText(AText);
Write('}');
end;
procedure TLaTeXWriter.DescrWriteVarEl(const AText: DOMString);
begin
Write('\var{');
DescrWriteText(AText);
Write('}');
end;
procedure TLaTeXWriter.DescrBeginLink(const AId: DOMString);
begin
FLink := Engine.ResolveLink(Module, UTF8Encode(AId));
// System.WriteLn('Link "', AId, '" => ', FLink);
end;
procedure TLaTeXWriter.DescrEndLink;
begin
WriteF(' (\pageref{%s})',[StripText(Flink)]);
end;
procedure TLaTeXWriter.DescrBeginURL(const AURL: DOMString);
begin
Inherited; // Save link
Write('\htmladdnormallink{');
end;
procedure TLaTeXWriter.DescrEndURL;
begin
WriteF('}{%s}',[LastURL]);
LastURL:='';
end;
procedure TLaTeXWriter.DescrWriteLinebreak;
begin
WriteLn('\\');
end;
procedure TLaTeXWriter.DescrBeginParagraph;
begin
// Do nothing
end;
procedure TLaTeXWriter.DescrEndParagraph;
begin
WriteLn('');
WriteLn('');
end;
procedure TLaTeXWriter.DescrBeginCode(HasBorder: Boolean;
const AHighlighterName: String);
begin
StartListing(HasBorder,'');
end;
procedure TLaTeXWriter.DescrWriteCodeLine(const ALine: String);
begin
WriteLn(ALine);
end;
procedure TLaTeXWriter.DescrEndCode;
begin
EndListing
end;
procedure TLaTeXWriter.DescrBeginOrderedList;
begin
WriteLn('\begin{enumerate}');
end;
procedure TLaTeXWriter.DescrEndOrderedList;
begin
WriteLn('\end{enumerate}');
end;
procedure TLaTeXWriter.DescrBeginUnorderedList;
begin
WriteLn('\begin{itemize}');
end;
procedure TLaTeXWriter.DescrEndUnorderedList;
begin
WriteLn('\end{itemize}');
end;
procedure TLaTeXWriter.DescrBeginDefinitionList;
begin
WriteLn('\begin{description}');
end;
procedure TLaTeXWriter.DescrEndDefinitionList;
begin
WriteLn('\end{description}');
end;
procedure TLaTeXWriter.DescrBeginListItem;
begin
Write('\item ');
end;
procedure TLaTeXWriter.DescrEndListItem;
begin
WriteLn('');
end;
procedure TLaTeXWriter.DescrBeginDefinitionTerm;
begin
Write('\item[');
end;
procedure TLaTeXWriter.DescrEndDefinitionTerm;
begin
WriteLn(']');
end;
procedure TLaTeXWriter.DescrBeginDefinitionEntry;
begin
// Do nothing
end;
procedure TLaTeXWriter.DescrEndDefinitionEntry;
begin
WriteLn('');
end;
procedure TLaTeXWriter.DescrBeginSectionTitle;
begin
Write('\subsection{');
end;
procedure TLaTeXWriter.DescrBeginSectionBody;
begin
WriteLn('}');
end;
procedure TLaTeXWriter.DescrEndSection;
begin
// Do noting
end;
procedure TLaTeXWriter.DescrBeginRemark;
begin
WriteLn('\begin{remark}');
end;
procedure TLaTeXWriter.DescrEndRemark;
begin
WriteLn('\end{remark}');
end;
procedure TLaTeXWriter.DescrBeginTable(ColCount: Integer; HasBorder: Boolean);
var
i: Integer;
begin
// !!!: How do we set the border?
Write('\begin{FPCltable}{');
for i := 1 to ColCount do
Write('l');
write('}{');
TableCaptionWritten:=False;
end;
procedure TLaTeXWriter.DescrEndTable;
begin
WriteLn('\end{FPCltable}');
end;
procedure TLaTeXWriter.DescrBeginTableCaption;
begin
// Do nothing.
end;
procedure TLaTeXWriter.DescrEndTableCaption;
begin
Write('}{table');
Inc(FTableCount);
Write(IntToStr(FTableCount));
Writeln('}');
TableCaptionWritten := True;
end;
procedure TLaTeXWriter.DescrBeginTableHeadRow;
begin
if not TableCaptionWritten then
DescrEndTableCaption;
TableRowStartFlag := True;
end;
procedure TLaTeXWriter.DescrEndTableHeadRow;
begin
WriteLn('\\ \hline');
end;
procedure TLaTeXWriter.DescrBeginTableRow;
begin
if not TableCaptionWritten then
DescrEndTableCaption;
TableRowStartFlag := True;
end;
procedure TLaTeXWriter.DescrEndTableRow;
begin
WriteLn('\\');
end;
procedure TLaTeXWriter.DescrBeginTableCell;
begin
if TableRowStartFlag then
TableRowStartFlag := False
else
Write(' & ');
end;
procedure TLaTeXWriter.DescrEndTableCell;
begin
// Do nothing
end;
procedure TLaTeXWriter.WriteLabel(const S: String);
begin
WriteLnF('\label{%s}', [LowerCase(StripText(s))]);
end;
procedure TLaTeXWriter.WriteIndex(const S: String);
begin
Write('\index{');
Write(EscapeText(s));
Writeln('}');
end;
procedure TLaTeXWriter.StartListing(Frames: Boolean; const name: String);
begin
FInVerbatim:=True;
if Not LaTexHighLight then
begin
Writeln('');
Writeln('\begin{verbatim}');
end
else
if Frames then
Writelnf('\begin{lstlisting}{%s}',[StripText(Name)])
else
Writelnf('\begin{lstlisting}[frame=]{%s}',[StripText(Name)]);
end;
procedure TLaTeXWriter.EndListing;
begin
FInVerbatim:=False;
If LaTexHighLight then
Writeln('\end{lstlisting}')
else
Writeln('\end{verbatim}')
end;
procedure TLaTeXWriter.WriteCommentLine;
const
CommentLine =
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%';
begin
Writeln(CommentLine);
end;
procedure TLaTeXWriter.WriteComment(Comment: String);
begin
Write('% ');
Writeln(Comment);
end;
procedure TLaTeXWriter.StartChapter(ChapterName: String);
begin
WriteCommentLine;
WriteComment(ChapterName);
WriteCommentLine;
Writeln('\chapter{'+EscapeText(ChapterName)+'}');
end;
procedure TLaTeXWriter.StartSection(SectionName: String);
begin
WriteCommentLine;
WriteComment(SectionName);
Writeln('\section{'+EscapeText(SectionName)+'}');
end;
procedure TLaTeXWriter.StartSubSection(SubSectionName: String);
begin
WriteComment(SubSectionName);
Writeln('\subsection{'+EscapeText(SubSectionName)+'}');
end;
procedure TLaTeXWriter.StartSubSubSection(SubSubSectionName: String);
begin
Writeln('\subsubsection{'+EscapeText(SubSubSectionName)+'}');
end;
procedure CreateLaTeXDocForPackage(APackage: TPasPackage; AEngine: TFPDocEngine);
var
Writer: TLaTeXWriter;
begin
Writer := TLaTeXWriter.Create(APackage, AEngine);
try
Writer.DoWriteDocumentation;
finally
Writer.Free;
end;
end;
procedure TLaTeXWriter.StartProcedure;
begin
Writeln('\begin{FPCList}');
InList:=True;
end;
procedure TLaTeXWriter.StartSynopsis;
begin
Writeln('\Synopsis');
end;
procedure TLaTeXWriter.StartDeclaration;
begin
Writeln('\Declaration ');
end;
procedure TLaTeXWriter.StartVisibility;
begin
Writeln('\Visibility');
end;
procedure TLaTeXWriter.StartDescription;
begin
Writeln('\Description');
end;
procedure TLaTeXWriter.StartErrors;
begin
Writeln('\Errors');
end;
procedure TLaTeXWriter.StartVersion;
begin
Writeln('\VersionInfo');
end;
procedure TLaTeXWriter.StartAccess;
begin
Writeln('\Access')
end;
procedure TLaTeXWriter.EndProcedure;
begin
InList:=False;
Writeln('\end{FPCList}');
end;
procedure TLaTeXWriter.StartProperty;
begin
Writeln('\begin{FPCList}');
InList:=True;
end;
procedure TLaTeXWriter.EndProperty;
begin
InList:=False;
Writeln('\end{FPCList}');
end;
procedure TLaTeXWriter.WriteExampleFile(FN: String);
begin
If (FN<>'') then
WritelnF('\FPCexample{%s}', [ChangeFileExt(FN,'')]);
end;
procedure TLaTeXWriter.StartOverview(const What: String; WithAccess: Boolean);
begin
If WithAccess then
begin
WriteLn('\begin{tabularx}{\textwidth}{lllX}');
WriteLnF('%s & %s & %s & %s \\ \hline',[EscapeText(SDocPage), EscapeText(What), EscapeText(SDocAccess), EscapeText(SDocDescription)])
end
else
begin
WriteLn('\begin{tabularx}{\textwidth}{llX}');
WriteLnF('%s & %s & %s \\ \hline',[EscapeText(SDocPage), EscapeText(What), EscapeText(SDocDescription)])
end;
end;
procedure TLaTeXWriter.EndOverview;
begin
WriteLn('\hline');
WriteLn('\end{tabularx}');
end;
procedure TLaTeXWriter.WriteOverviewMember(const ALabel, AName, Access,
ADescr: String);
begin
WriteLnF('\pageref{%s} & %s & %s & %s \\',[ALabel,EscapeText(AName),Access,ADescr]);
end;
procedure TLaTeXWriter.WriteOverviewMember(const ALabel, AName, ADescr: String);
begin
WriteLnF('\pageref{%s} & %s & %s \\',[ALabel,EscapeText(AName),ADescr]);
end;
class function TLaTeXWriter.FileNameExtension: String;
begin
Result:=TexExtension;
end;
procedure TLaTeXWriter.StartSeealso;
begin
If not InList then
begin
Writeln('');
Writeln('\begin{FPCList}');
end;
Writeln('\SeeAlso');
end;
procedure TLaTeXWriter.EndSeealso;
begin
If Not InList then
Writeln('\end{FPCList}');
end;
procedure TLaTeXWriter.StartUnitOverview(AModuleName, AModuleLabel: String);
begin
WriteLnF('\begin{FPCltable}{lr}{%s}{%s:0units}',
[Format(SDocUsedUnitsByUnitXY, [EscapeText(AModuleName)]), StripText(AModuleName)]);
WriteLn('Name & Page \\ \hline');
end;
procedure TLaTeXWriter.WriteUnitEntry(UnitRef: TPasType);
begin
WriteLnF('%s\index{unit!%s} & \pageref{%s} \\',
[EscapeText(UnitRef.Name), EscapeText(UnitRef.Name), StripText(GetLabel(UnitRef))]);
end;
procedure TLaTeXWriter.EndUnitOverview;
begin
WriteLn('\end{FPCltable}');
end;
function TLaTeXWriter.InterPretOption(const Cmd, Arg: String): boolean;
begin
Result:=True;
if (cmd= '--latex-highlight') then
LatexHighLight:=True
else if Cmd = '--latex-extension' then
TexExtension:=Arg
else if Cmd = '--latex--verbatim-length' then
MaxVerbatimLength:=StrToInt(Arg)
else if Cmd = '--image-dir' then
ImageDir:=Arg
else
Result:=False;
end;
Resourcestring
SLatexHighlightDocs = 'Use the syntax highlighter for declarations.';
SLatexExtensionDocs = 'Specify the extension for the latex files.';
SLatexVerbatimLengthDocs = 'Specify maximum line length for verbatim environments (default 64).';
SLatexImageDirDocs = 'Specify the directory where the images are stored.';
class procedure TLaTeXWriter.Usage(List: TStrings);
begin
Inherited;
List.Add('--latex-highlight');
List.Add(SLatexHighlightDocs);
List.Add('--latex-extension=ext');
List.Add(SLatexExtensionDocs);
List.Add('--latex-verbatim-length=len');
List.Add(SLatexVerbatimLengthDocs);
List.Add('--image-dir=dir');
List.Add(SLatexImageDirDocs);
end;
initialization
// Do not localize.
RegisterWriter(TLaTeXWriter,'latex','Latex output using fpc.sty class.');
finalization
UnRegisterWriter('latex');
end.