diff --git a/components/fpspreadsheet/examples/wikitablemaker/synhighlighterwikitable.pas b/components/fpspreadsheet/examples/wikitablemaker/synhighlighterwikitable.pas
index 8dd650e9a..16a20bcb1 100644
--- a/components/fpspreadsheet/examples/wikitablemaker/synhighlighterwikitable.pas
+++ b/components/fpspreadsheet/examples/wikitablemaker/synhighlighterwikitable.pas
@@ -44,11 +44,11 @@ type
FNumberAttri: TSynHighlighterAttributes;
FSpaceAttri: TSynHighlighterAttributes;
FSymbolAttri: TSynHighlighterAttributes;
- (*
FIdentifierAttri: TSynHighlighterAttributes;
+ FStringAttri: TSynHighlighterAttributes;
+ (*
FKeyAttri: TSynHighlighterAttributes;
FNumberAttri: TSynHighlighterAttributes;
- FStringAttri: TSynHighlighterAttributes;
*)
procedure InitIdent;
function IdentKind(MayBe: PChar): TtkTokenKind;
@@ -59,13 +59,13 @@ type
procedure AmpersandProc;
procedure BarProc;
procedure BeginProc;
- procedure CommentProc;
procedure CRProc;
procedure ExclamProc;
procedure LFProc;
procedure IdentProc;
procedure NullProc;
procedure NumberProc;
+ procedure OpenBraceProc;
procedure SpaceProc;
procedure StringProc;
procedure TextProc;
@@ -86,10 +86,14 @@ type
published
property CommentAttri: TSynHighlighterAttributes
read FCommentAttri write FCommentAttri;
+ property IdentifierAttri: TSynHighlighterAttributes
+ read FIdentifierAttri write FIdentifierAttri;
property NumberAttri: TSynHighlighterAttributes
read FNumberAttri write FNumberAttri;
property SpaceAttri: TSynHighlighterAttributes
read FSpaceAttri write FSpaceAttri;
+ property StringAttri: TSynHighlighterAttributes
+ read FStringAttri write FStringAttri;
property SymbolAttri: TSynHighlighterAttributes
read FSymbolAttri write FSymbolAttri;
@@ -312,16 +316,19 @@ begin
FSymbolAttri.Foreground := clPurple;
AddAttribute(fSymbolAttri);
- (*
- fIdentifierAttri := TSynHighlighterAttributes.Create(@SYNS_AttrIdentifier, SYNS_XML_AttrIdentifier);
+ FIdentifierAttri := TSynHighlighterAttributes.Create(@SYNS_AttrIdentifier, SYNS_XML_AttrIdentifier);
+ FIdentifierAttri.Foreground := clNavy;
+ FIdentifierAttri.Style := [fsBold];
AddAttribute(fIdentifierAttri);
+ FStringAttri := TSynHighlighterAttributes.Create(@SYNS_AttrString, SYNS_XML_AttrString);
+ FStringAttri.Foreground := clOlive;
+ AddAttribute(FStringAttri);
+ (*
fKeyAttri := TSynHighlighterAttributes.Create(@SYNS_AttrKey, SYNS_XML_AttrKey);
fKeyAttri.Style := [fsBold];
AddAttribute(fKeyAttri);
- fStringAttri := TSynHighlighterAttributes.Create(@SYNS_AttrString, SYNS_XML_AttrString);
- AddAttribute(fStringAttri);
*)
SetAttributesOnChange(@DefHighlightChange);
@@ -362,30 +369,6 @@ begin
end;
end;
-procedure TSynWikitableSyn.CommentProc;
-begin
- FTokenID := tkComment;
-
- if (FLine[Run] in [#0, #10, #13]) then begin
- FProcTable[FLine[Run]];
- Exit;
- end;
-
- while not (FLine[Run] in [#0, #10, #13]) do begin
- if (FLine[Run] = '>') and (FLine[Run - 1] = '-') and (FLine[Run - 2] = '-')
- then begin
- FRange := rsText;
- inc(Run);
- {
- if TopHtmlCodeFoldBlockType = cfbtHtmlComment then
- EndHtmlNodeCodeFoldBlock;
- }
- break;
- end;
- inc(Run);
- end;
-end;
-
procedure TSynWikiTableSyn.CRProc;
begin
FTokenID := tkSpace;
@@ -407,10 +390,10 @@ begin
SYN_ATTR_SYMBOL : Result := FSymbolAttri;
SYN_ATTR_NUMBER : Result := FNumberAttri;
SYN_ATTR_WHITESPACE : Result := FSpaceAttri;
- (*
SYN_ATTR_IDENTIFIER : Result := FIdentifierAttri;
- SYN_ATTR_KEYWORD : Result := FKeyAttri;
SYN_ATTR_STRING : Result := FStringAttri;
+ (*
+ SYN_ATTR_KEYWORD : Result := FKeyAttri;
*)
else
Result := nil;
@@ -434,16 +417,16 @@ end;
function TSynWikiTableSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
case GetTokenID of
- tkComment : Result := FCommentAttri;
- tkSymbol : Result := FSymbolAttri;
- tkNumber : Result := FNumberAttri;
- tkSpace : Result := FSpaceAttri;
+ tkComment : Result := FCommentAttri;
+ tkSymbol : Result := FSymbolAttri;
+ tkNumber : Result := FNumberAttri;
+ tkSpace : Result := FSpaceAttri;
+ tkIdentifier : Result := FIdentifierAttri;
+ tkString : Result := FStringAttri;
{
- tkIdentifier: Result := FIdentifierAttri;
- tkKey : Result := FKeyAttri;
- tkNumber : Result := FNumberAttri;
- tkString : Result := FStringAttri;
- tkUnknown : Result := FIdentifierAttri;
+ tkKey : Result := FKeyAttri;
+ tkNumber : Result := FNumberAttri;
+ tkUnknown : Result := FIdentifierAttri;
}
else
Result := nil;
@@ -479,7 +462,7 @@ begin
FToIdent := MayBe;
HashKey := KeyHash(MayBe);
if (HashKey >= 16) and (HashKey <= 275) then
- Result := fIdentFuncTable[HashKey]()
+ Result := FIdentFuncTable[HashKey]()
else
Result := tkIdentifier;
end;
@@ -574,7 +557,7 @@ begin
'0'..'9' : FProcTable[ch] := @NumberProc;
'A'..'Z', 'a'..'z', '_','@' : FProcTable[ch] := @IdentProc;
'&' : FProcTable[ch] := @AmpersandProc;
- '<' : FProcTable[ch] := @CommentProc;
+ '<' : FProcTable[ch] := @OpenBraceProc;
'{' : FProcTable[ch] := @BeginProc;
'|' : FProcTable[ch] := @BarProc;
'!' : FProcTable[ch] := @ExclamProc;
@@ -621,7 +604,7 @@ begin
FTokenPos := Run;
case FRange of
rsText : TextProc;
- rsComment : CommentProc;
+ rsComment : OpenBraceProc;
else FProcTable[FLine[Run]];
end;
@@ -651,6 +634,58 @@ begin
end;
end;
+procedure TSynWikitableSyn.OpenBraceProc;
+begin
+ if (FLine[Run+1] = '!') and (FLine[Run+2] = '-') and (FLine[Run+3] = '-') then
+ begin
+ FTokenID := tkComment;
+ while not (FLine[Run] in [#0, #10, #13]) do begin
+ if (FLine[Run] = '>') and (FLine[Run - 1] = '-') and (FLine[Run - 2] = '-')
+ then begin
+ FRange := rsText;
+ inc(Run);
+ {
+ if TopHtmlCodeFoldBlockType = cfbtHtmlComment then
+ EndHtmlNodeCodeFoldBlock;
+ }
+ break;
+ end;
+ inc(Run);
+ end;
+ end else begin
+ FTokenID := tkSymbol;
+ while not (FLine[Run] in [#0, #10, #13]) do begin
+ if FLine[Run] = '>' then begin
+ FRange := rsText;
+ inc(Run);
+ break;
+ end;
+ inc(Run);
+ end;
+ end;
+
+(*
+ if (FLine[Run] in [#0, #10, #13]) then begin
+ FProcTable[FLine[Run]];
+ Exit;
+ end;
+
+ while not (FLine[Run] in [#0, #10, #13]) do begin
+ if (FLine[Run] = '>') and (FLine[Run - 1] = '-') and (FLine[Run - 2] = '-')
+ then begin
+ FRange := rsText;
+ inc(Run);
+ {
+ if TopHtmlCodeFoldBlockType = cfbtHtmlComment then
+ EndHtmlNodeCodeFoldBlock;
+ }
+ break;
+ end;
+ inc(Run);
+ end;
+ *)
+end;
+
procedure TSynWikiTableSyn.SetLine(const NewValue: String; LineNumber: Integer);
begin
inherited;
diff --git a/components/fpspreadsheet/examples/wikitablemaker/wtmain.lfm b/components/fpspreadsheet/examples/wikitablemaker/wtmain.lfm
index 0554a6a71..7a461d8d4 100644
--- a/components/fpspreadsheet/examples/wikitablemaker/wtmain.lfm
+++ b/components/fpspreadsheet/examples/wikitablemaker/wtmain.lfm
@@ -717,7 +717,6 @@ object MainFrm: TMainFrm
''
)
VisibleSpecialChars = [vscSpace, vscTabAtLast]
- ReadOnly = True
SelectedColor.FrameEdges = sfeAround
SelectedColor.BackPriority = 50
SelectedColor.ForePriority = 50
@@ -3794,7 +3793,9 @@ object MainFrm: TMainFrm
DefaultFilter = 'Cascading Stylesheets (*.css)|*.css'
Enabled = False
CommentAttri.FrameEdges = sfeAround
+ IdentifierAttri.Foreground = clOlive
IdentifierAttri.FrameEdges = sfeAround
+ IdentifierAttri.Style = [fsBold]
KeyAttri.FrameEdges = sfeAround
NumberAttri.Foreground = clFuchsia
NumberAttri.FrameEdges = sfeAround
diff --git a/components/fpspreadsheet/examples/wikitablemaker/wtmain.pas b/components/fpspreadsheet/examples/wikitablemaker/wtmain.pas
index 0523592d3..d1a6fbe52 100644
--- a/components/fpspreadsheet/examples/wikitablemaker/wtmain.pas
+++ b/components/fpspreadsheet/examples/wikitablemaker/wtmain.pas
@@ -593,7 +593,7 @@ begin
// Create the syntax highlighter
FHighlighter := TSynWikitableSyn.Create(self);
SynEdit.Highlighter := FHighlighter;
-// SynEdit.Highlighter := SynCSSSyn1;
+ //SynEdit.Highlighter := SynCSSSyn1;
// Adjust format toolbar height, looks strange at 120 dpi
//FormatToolbar.Height := FontCombobox.Height + 2*FontCombobox.Top;
diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas
index d77962bbc..0bc45e38c 100755
--- a/components/fpspreadsheet/fpsopendocument.pas
+++ b/components/fpspreadsheet/fpsopendocument.pas
@@ -3786,6 +3786,7 @@ var
rowsSpannedStr: String;
spannedStr: String;
r1,c1,r2,c2: Cardinal;
+ str: ansistring;
begin
Unused(AStream, ACell);
Unused(ARow, ACol);
@@ -3806,19 +3807,22 @@ begin
end else
spannedStr := '';
+ // Check for invalid characters
+ str := AValue;
+ if not ValidXMLText(str) then
+ Workbook.AddErrorMsg(
+ 'Invalid character(s) in cell %s.', [
+ GetCellString(ARow, ACol)
+ ]);
+
+ // Write it ...
AppendToStream(AStream, Format(
'' +
'%s'+
'', [
lStyle, spannedStr,
- UTF8TextToXMLText(AValue)
+ str
]));
- {
- AppendToStream(AStream,
- '' +
- '' + UTF8TextToXMLText(AValue) + '' +
- '');
- }
end;
procedure TsSpreadOpenDocWriter.WriteNumber(AStream: TStream; const ARow,
diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas
index 65965d228..ef4991cfd 100644
--- a/components/fpspreadsheet/fpsutils.pas
+++ b/components/fpspreadsheet/fpsutils.pas
@@ -135,6 +135,8 @@ function HTMLLengthStrToPts(AValue: String): Double;
function HTMLColorStrToColor(AValue: String): TsColorValue;
function ColorToHTMLColorStr(AValue: TsColorValue; AExcelDialect: Boolean = false): String;
function UTF8TextToXMLText(AText: ansistring): ansistring;
+function ValidXMLText(var AText: ansistring; ReplaceSpecialChars: Boolean = true): Boolean;
+
function TintedColor(AColor: TsColorValue; tint: Double): TsColorValue;
function HighContrastColor(AColorValue: TsColorValue): TsColor;
@@ -1576,6 +1578,39 @@ begin
Result:=WrkStr;
end;
+{@@ ----------------------------------------------------------------------------
+ Checks a string for characters that are not permitted in XML strings.
+ The function returns FALSE if a character <#32 is contained (except for
+ #9, #10, #13), TRUE otherwise. Invalid characters are replaced by a box symbol.
+
+ If ReplaceSpecialChars is TRUE, some other characters are converted
+ to valid HTML codes by calling UTF8TextToXMLText
+
+ @param AText String to be checked. Is replaced by valid string.
+ @param ReplaceSpecialChars Special characters are replaced by their HTML
+ codes (e.g. '>' --> '>')
+ @return FALSE if characters < #32 were replaced, TRUE otherwise.
+-------------------------------------------------------------------------------}
+function ValidXMLText(var AText: ansistring;
+ ReplaceSpecialChars: Boolean = true): Boolean;
+const
+ BOX = #$E2#$8E#$95;
+var
+ i: Integer;
+begin
+ Result := true;
+ for i := Length(AText) downto 1 do
+ if (AText[i] < #32) and not (AText[i] in [#9, #10, #13]) then begin
+ // Replace invalid character by box symbol
+ Delete(AText, i, 1);
+ Insert(BOX, AText, i);
+// AText[i] := '?';
+ Result := false;
+ end;
+ if ReplaceSpecialChars then
+ AText := UTF8TextToXMLText(AText);
+end;
+
{******************************************************************************}
{******************************************************************************}
diff --git a/components/fpspreadsheet/tests/errortests.pas b/components/fpspreadsheet/tests/errortests.pas
index d980a3d13..713c01734 100644
--- a/components/fpspreadsheet/tests/errortests.pas
+++ b/components/fpspreadsheet/tests/errortests.pas
@@ -163,6 +163,24 @@ begin
end;
end;
+ // Test 5: cell text contains forbidden XML character
+ if (TTestFormat(AFormat) in [sfOOXML, sfOpenDocument]) then begin
+ s := #19'Standard';
+ MyWorkbook := TsWorkbook.Create;
+ try
+ MyWorksheet := MyWorkbook.AddWorksheet(ERROR_SHEET);
+ Myworksheet.WriteUTF8Text(0, 0, s);
+ TempFile := NewTempFile;
+ Myworkbook.WriteToFile(TempFile, AFormat, true);
+ ErrList.Text := MyWorkbook.ErrorMsg;
+ CheckEquals(1, ErrList.Count, 'Error count mismatch in test 5');
+ finally
+ MyWorkbook.Free;
+ DeleteFile(TempFile);
+ end;
+ end else
+ Ignore('Test 5 is no error condition for this format');
+
finally
ErrList.Free;
end;
diff --git a/components/fpspreadsheet/wikitable.pas b/components/fpspreadsheet/wikitable.pas
index 95ace98e6..2766352c9 100644
--- a/components/fpspreadsheet/wikitable.pas
+++ b/components/fpspreadsheet/wikitable.pas
@@ -397,7 +397,7 @@ const
PIPE_CHAR: array[boolean] of String = ('|', '!');
var
i, j: cardinal;
- lCurStr: string = '';
+ lCurStr: ansistring = '';
lCurUsedFormatting: TsUsedFormattingFields;
lCurColor: TsColor;
lStyleStr: String;
@@ -451,7 +451,15 @@ begin
begin
lCell := FWorksheet.FindCell(i, j);
lCurStr := FWorksheet.ReadAsUTF8Text(lCell);
- if lCurStr = '' then lCurStr := ' ';
+// if lCurStr = '' then lCurStr := ' ';
+
+ // Check for invalid characters
+ if not ValidXMLText(lCurStr, false) then
+ Workbook.AddErrorMsg(
+ 'Invalid character(s) in cell %s.', [
+ GetCellString(i, j)
+ ]);
+
lStyleStr := '';
lColSpanStr := '';
lRowSpanStr := '';
diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas
index 90f3a7485..74528190f 100755
--- a/components/fpspreadsheet/xlsxooxml.pas
+++ b/components/fpspreadsheet/xlsxooxml.pas
@@ -170,7 +170,7 @@ type
implementation
uses
- variants, fileutil, strutils, math, fpsStreams, fpsNumFormatParser;
+ variants, fileutil, strutils, math, lazutf8, fpsStreams, fpsNumFormatParser;
const
{ OOXML general XML constants }
@@ -2769,9 +2769,15 @@ begin
else
ResultingValue:=AValue;
+ if not ValidXMLText(ResultingValue) then
+ Workbook.AddErrorMsg(
+ 'Invalid character(s) in cell %s.', [
+ GetCellString(ARow, ACol)
+ ]);
+
AppendToStream(FSSharedStrings,
'' +
- '' + UTF8TextToXMLText(ResultingValue) + '' +
+ '' + ResultingValue + '' +
'');
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);