Skip to content

Commit ac2f138

Browse files
committed
Major overhaul of active text HTML renderer
Code now based in part on the REML active text writer. The REML based code was simplified due to the fact that the HTML renderer has tags that match the ekBlock and ekDocument elements, whereas REML doesn't. HTML code is now indented to highlight the document structure (although this doesn't matter really since the code is never displayed to the user).
1 parent 2715585 commit ac2f138

File tree

1 file changed

+79
-72
lines changed

1 file changed

+79
-72
lines changed

Src/ActiveText.UHTMLRenderer.pas

Lines changed: 79 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -47,43 +47,43 @@ TTagInfo = class(TObject)
4747
TCSSStyles = class(TObject)
4848
strict private
4949
var
50-
fWrapperClass: string;
5150
fElemClassMap: array[TActiveTextActionElemKind] of string;
5251
procedure SetElemClass(ElemKind: TActiveTextActionElemKind;
5352
const Value: string); inline;
5453
function GetElemClass(ElemKind: TActiveTextActionElemKind): string;
5554
inline;
5655
public
5756
constructor Create;
58-
property WrapperClass: string read fWrapperClass write fWrapperClass;
5957
property ElemClasses[Kind: TActiveTextActionElemKind]: string
6058
read GetElemClass write SetElemClass;
6159
end;
6260
strict private
6361
var
6462
fCSSStyles: TCSSStyles;
6563
fBuilder: TStringBuilder;
66-
fInBlock: Boolean;
64+
fLevel: Integer;
6765
fTagInfoMap: TTagInfoMap;
66+
fIsStartOfTextLine: Boolean;
6867
fLINestingDepth: Cardinal;
68+
const
69+
IndentMult = 2;
6970
procedure InitialiseTagInfoMap;
70-
procedure InitialiseRender;
71-
procedure RenderTextElem(Elem: IActiveTextTextElem);
72-
procedure RenderBlockActionElem(Elem: IActiveTextActionElem);
73-
procedure RenderInlineActionElem(Elem: IActiveTextActionElem);
74-
procedure FinaliseRender;
71+
function RenderTag(const TagElem: IActiveTextActionElem): string;
72+
function RenderText(const TextElem: IActiveTextTextElem): string;
7573
function MakeOpeningTag(const Elem: IActiveTextActionElem): string;
7674
function MakeClosingTag(const Elem: IActiveTextActionElem): string;
7775
public
7876
constructor Create;
7977
destructor Destroy; override;
8078
function Render(ActiveText: IActiveText): string;
81-
property Styles: TCSSStyles read fCSSStyles;
8279
end;
8380

8481

8582
implementation
8683

84+
uses
85+
UConsts, UIStringList, UStrUtils;
86+
8787

8888
{ TActiveTextHTML }
8989

@@ -107,22 +107,6 @@ destructor TActiveTextHTML.Destroy;
107107
inherited;
108108
end;
109109

110-
procedure TActiveTextHTML.FinaliseRender;
111-
begin
112-
fBuilder.AppendLine(THTML.ClosingTag('div'));
113-
end;
114-
115-
procedure TActiveTextHTML.InitialiseRender;
116-
var
117-
WrapperClassAttr: IHTMLAttributes;
118-
begin
119-
if fCSSStyles.WrapperClass <> '' then
120-
WrapperClassAttr := THTMLAttributes.Create('class', fCSSStyles.WrapperClass)
121-
else
122-
WrapperClassAttr := nil;
123-
fBuilder.AppendLine(THTML.OpeningTag('div', WrapperClassAttr));
124-
end;
125-
126110
procedure TActiveTextHTML.InitialiseTagInfoMap;
127111
var
128112
NullAttrs: TTagInfo.TTagAttrCallback;
@@ -131,7 +115,10 @@ procedure TActiveTextHTML.InitialiseTagInfoMap;
131115
ElemKind: TActiveTextActionElemKind;
132116
const
133117
Tags: array[TActiveTextActionElemKind] of string = (
134-
'a', 'strong', 'em', 'var', 'p', 'span', 'h2', 'code', 'ul', 'ol', 'li'
118+
'a' {ekLink}, 'strong' {ekStrong}, 'em' {ekEm}, 'var' {ekVar}, 'p' {ekPara},
119+
'span' {ekWarning}, 'h2' {ekHeading}, 'code' {ekMono},
120+
'ul' {ekUnorderedList}, 'ol' {ekUnorderedList}, 'li' {ekListItem},
121+
'div' {ekBlock}, 'div' {ekDocument}
135122
);
136123
begin
137124
NullAttrs := function(Elem: IActiveTextActionElem): IHTMLAttributes
@@ -178,80 +165,100 @@ function TActiveTextHTML.MakeOpeningTag(const Elem: IActiveTextActionElem):
178165

179166
function TActiveTextHTML.Render(ActiveText: IActiveText): string;
180167
var
181-
Elem: IActiveTextElem;
182-
TextElem: IActiveTextTextElem;
183-
ActionElem: IActiveTextActionElem;
168+
Elem: IActiveTextElem; // each element in active text object
169+
TextElem: IActiveTextTextElem; // an active text text element
170+
TagElem: IActiveTextActionElem; // an active text action element
171+
Text: string;
172+
SrcLines: IStringList;
173+
SrcLine: string;
174+
DestLines: IStringList;
175+
DestLine: string;
184176
begin
185-
fBuilder.Clear;
186-
fInBlock := False;
187-
InitialiseRender;
177+
if ActiveText.IsEmpty then
178+
Exit('');
179+
Text := '';
180+
fLevel := 0;
188181
for Elem in ActiveText do
189182
begin
190183
if Supports(Elem, IActiveTextTextElem, TextElem) then
191-
RenderTextElem(TextElem)
192-
else if Supports(Elem, IActiveTextActionElem, ActionElem) then
193-
begin
194-
if TActiveTextElemCaps.DisplayStyleOf(ActionElem.Kind) = dsBlock then
195-
RenderBlockActionElem(ActionElem)
196-
else
197-
RenderInlineActionElem(ActionElem);
198-
end;
184+
Text := Text + RenderText(TextElem)
185+
else if Supports(Elem, IActiveTextActionElem, TagElem) then
186+
Text := Text + RenderTag(TagElem);
199187
end;
200-
FinaliseRender;
201-
Result := fBuilder.ToString;
188+
SrcLines := TIStringList.Create(Text, EOL, False);
189+
DestLines := TIStringList.Create;
190+
for SrcLine in SrcLines do
191+
begin
192+
DestLine := StrTrimRight(SrcLine);
193+
if not StrIsEmpty(DestLine) then
194+
DestLines.Add(DestLine);
195+
end;
196+
Result := DestLines.GetText(EOL, False);
202197
end;
203198

204-
procedure TActiveTextHTML.RenderBlockActionElem(Elem: IActiveTextActionElem);
199+
function TActiveTextHTML.RenderTag(const TagElem: IActiveTextActionElem):
200+
string;
205201
begin
206-
case Elem.State of
207-
fsOpen:
208-
begin
209-
if Elem.Kind = ekListItem then
210-
Inc(fLINestingDepth);
211-
fBuilder.Append(MakeOpeningTag(Elem));
212-
fInBlock := True;
213-
end;
202+
Result := '';
203+
case TagElem.State of
214204
fsClose:
215205
begin
216-
fInBlock := False;
217-
fBuilder.AppendLine(MakeClosingTag(Elem));
218-
if Elem.Kind = ekListItem then
219-
Dec(fLINestingDepth);
206+
Result := MakeClosingTag(TagElem);
207+
if TActiveTextElemCaps.DisplayStyleOf(TagElem.Kind) = dsBlock then
208+
begin
209+
Dec(fLevel);
210+
Result := EOL + StrOfSpaces(IndentMult * fLevel) + Result + EOL;
211+
fIsStartOfTextLine := True;
212+
end;
220213
end;
221-
end;
222-
end;
223-
224-
procedure TActiveTextHTML.RenderInlineActionElem(Elem: IActiveTextActionElem);
225-
begin
226-
if not fInBlock and (fLINestingDepth = 0) then
227-
Exit;
228-
case Elem.State of
229214
fsOpen:
230-
fBuilder.Append(MakeOpeningTag(Elem));
231-
fsClose:
232-
fBuilder.Append(MakeClosingTag(Elem));
215+
begin
216+
Result := MakeOpeningTag(TagElem);
217+
if TActiveTextElemCaps.DisplayStyleOf(TagElem.Kind) = dsBlock then
218+
begin
219+
Result := EOL + StrOfSpaces(IndentMult * fLevel) + Result + EOL;
220+
Inc(fLevel);
221+
fIsStartOfTextLine := True;
222+
end
223+
else if TActiveTextElemCaps.DisplayStyleOf(TagElem.Kind) = dsInline then
224+
begin
225+
if fIsStartOfTextLine then
226+
begin
227+
Result := StrOfSpaces(IndentMult * fLevel) + Result;
228+
fIsStartOfTextLine := False;
229+
end;
230+
end;
231+
end;
233232
end;
234233
end;
235234

236-
procedure TActiveTextHTML.RenderTextElem(Elem: IActiveTextTextElem);
235+
function TActiveTextHTML.RenderText(const TextElem: IActiveTextTextElem):
236+
string;
237237
begin
238-
if not fInBlock and (fLINestingDepth = 0) then
239-
Exit;
240-
fBuilder.Append(THTML.Entities(Elem.Text));
238+
if fIsStartOfTextLine then
239+
begin
240+
Result := StrOfSpaces(IndentMult * fLevel);
241+
fIsStartOfTextLine := False;
242+
end
243+
else
244+
Result := '';
245+
Result := Result + THTML.Entities(TextElem.Text);
241246
end;
242247

243248
{ TActiveTextHTML.TCSSStyles }
244249

245250
constructor TActiveTextHTML.TCSSStyles.Create;
246251
const
247252
DefaultClasses: array[TActiveTextActionElemKind] of string = (
248-
'external-link', '', '', '', '', 'warning', '', '', '', '', ''
253+
'external-link' {ekLink}, '' {ekStrong}, '' {ekEm}, '' {ekVar}, '' {ekPara},
254+
'warning' {ekWarning}, '' {ekHeading}, '' {ekMono}, '' {ekUnorderedList},
255+
'' {ekOrderedList}, '' {ekListItem}, '' {ekBlock},
256+
'active-text' {ekDocument}
249257
);
250258
var
251259
ElemKind: TActiveTextActionElemKind;
252260
begin
253261
inherited Create;
254-
fWrapperClass := 'active-text';
255262
for ElemKind := Low(TActiveTextActionElemKind)
256263
to High(TActiveTextActionElemKind) do
257264
SetElemClass(ElemKind, DefaultClasses[ElemKind]);

0 commit comments

Comments
 (0)