fpspreadsheet: Fix bug in formula parser when scanning of ods cell addresses (incorrect extraction of row index).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6577 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-08-09 22:00:21 +00:00
parent db4fd514dc
commit ba5e4da9be
2 changed files with 90 additions and 81 deletions

View File

@ -953,7 +953,7 @@ end;
It has the structure [sheet1.C1R1:sheet2.C2R2] }
function TsExpressionScanner.DoCellRangeODS: TsTokenType;
type
TScannerStateODS = (ssSheet1, ssCol1, ssRow1, ssSheet2, ssCol2, ssRow2);
TScannerStateODS = (ssInSheet1, ssInCol1, ssInRow1, ssInSheet2, ssInCol2, ssInRow2);
var
C: Char;
prevC: Char;
@ -968,7 +968,7 @@ begin
FCellRange.Col2 := Cardinal(-1);
FFlags := rfAllRel;
state := ssSheet1;
state := ssInSheet1;
FToken := '';
C := NextPos;
prevC := #0;
@ -976,33 +976,33 @@ begin
case C of
cNULL: ScanError(rsUnexpectedEndOfExpression);
'.': begin
if (state = ssSheet1) then
if (state = ssInSheet1) then
begin
FSheet1 := FToken;
state := ssCol1;
state := ssInCol1;
end else
if (state = ssSheet2) then
if (state = ssInSheet2) then
begin
FSheet2 := FToken;
state := ssCol2;
state := ssInCol2;
end else
ScanError(rsIllegalODSCellRange);
FToken := '';
val := 0;
end;
':': if (state = ssRow1) then
':': if (state = ssInRow1) then
begin
FCellRange.Row1 := val-1;
state := ssSheet2;
state := ssInSheet2;
FToken := '';
end else
ScanError(rsIllegalODSCellRange);
'$': case state of
ssCol1: if prevC = '.' then Exclude(FFlags, rfRelCol) else Exclude(FFlags, rfRelRow);
ssCol2: if prevC = '.' then Exclude(FFlags, rfRelCol2) else Exclude(FFlags, rfRelRow2);
ssInCol1: if prevC = '.' then Exclude(FFlags, rfRelCol) else Exclude(FFlags, rfRelRow);
ssInCol2: if prevC = '.' then Exclude(FFlags, rfRelCol2) else Exclude(FFlags, rfRelRow2);
end;
else
if (state in [ssSheet1, ssSheet2]) then
if (state in [ssInSheet1, ssInSheet2]) then
FToken := FToken + C
else
case C of
@ -1011,16 +1011,21 @@ begin
'a'..'z':
val := val*10 + ord(C) - ord('a');
'0'..'9':
if state = ssCol1 then begin
if state = ssInCol1 then begin
FCellRange.Col1 := val;
val := ord(C) - ord('0');
state := ssRow1;
val := (ord(C) - ord('0'));
state := ssInRow1;
end else
if state = ssCol2 then begin
if state = ssInRow1 then
val := val*10 + (ord(C) - ord('0'))
else
if state = ssInCol2 then begin
FCellRange.Col2 := val;
val := ord(C) - ord('0');
state := ssRow2;
end;
val := (ord(C) - ord('0'));
state := ssInRow2;
end else
if state = ssInRow2 then
val := val*10 + (ord(C) - ord('0'));
end;
end;
prevC := C;
@ -1029,9 +1034,9 @@ begin
if C <> ']' then
ScanError(Format(rsRightSquareBracketExpected, [FPos, C]));
case state of
ssRow1:
ssInRow1:
if val > 0 then FCellRange.Row1 := val - 1 else ScanError(rsIllegalODSCellRange);
ssRow2:
ssInRow2:
if val > 0 then FCellRange.Row2 := val - 1 else ScanError(rsIllegalODSCellRange);
end;
if FCellRange.Col2 = Cardinal(-1) then Exclude(FFlags, rfRelCol2);

View File

@ -2463,74 +2463,78 @@ begin
hasFormula := false;
// Read formula results
if hasFormula then TsWorkbook(FWorkbook).LockFormulas;
valueType := GetAttrValue(ACellNode, 'office:value-type');
valueStr := GetAttrValue(ACellNode, 'office:value');
calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type');
// ODS wants a 0 in the NumberValue field in case of an error. If there is
// no error, this value will be corrected below.
cell^.NumberValue := 0.0;
// (a) number value
if (valueType = 'float') then
begin
if UpperCase(valueStr) = '1.#INF' then
TsWorksheet(FWorksheet).WriteNumber(cell, 1.0/0.0)
else
// Prevent formulas from being erased when formula results are written to cells
TsWorkbook(FWorkbook).LockFormulas;
try
valueType := GetAttrValue(ACellNode, 'office:value-type');
valueStr := GetAttrValue(ACellNode, 'office:value');
calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type');
// ODS wants a 0 in the NumberValue field in case of an error. If there is
// no error, this value will be corrected below.
cell^.NumberValue := 0.0;
// (a) number value
if (valueType = 'float') then
begin
floatValue := StrToFloat(valueStr, FPointSeparatorSettings);
TsWorksheet(FWorksheet).WriteNumber(cell, floatValue);
end;
if IsDateTimeFormat(fmt^.NumberFormat) then
begin
cell^.ContentType := cctDateTime;
// No datemode correction for intervals and for time-only values
if (fmt^.NumberFormat = nfTimeInterval) or (cell^.NumberValue < 1) then
cell^.DateTimeValue := cell^.NumberValue
if UpperCase(valueStr) = '1.#INF' then
TsWorksheet(FWorksheet).WriteNumber(cell, 1.0/0.0)
else
case FDateMode of
dmODS1899: cell^.DateTimeValue := cell^.NumberValue + DATEMODE_1899_BASE;
dmODS1900: cell^.DateTimeValue := cell^.NumberValue + DATEMODE_1900_BASE;
dmODS1904: cell^.DateTimeValue := cell^.NumberValue + DATEMODE_1904_BASE;
end;
end;
end else
// (b) Date/time value
if (valueType = 'date') or (valueType = 'time') then
begin
floatValue := ExtractDateTimeFromNode(ACellNode, fmt^.NumberFormat, fmt^.NumberFormatStr);
TsWorksheet(FWorkSheet).WriteDateTime(cell, floatValue);
end else
// (c) text
if (valueType = 'string') and (calcextValueType <> 'error') then
begin
node := ACellNode.FindNode('text:p');
if (node <> nil) and (node.FirstChild <> nil) then
begin
floatValue := StrToFloat(valueStr, FPointSeparatorSettings);
TsWorksheet(FWorksheet).WriteNumber(cell, floatValue);
end;
if IsDateTimeFormat(fmt^.NumberFormat) then
begin
cell^.ContentType := cctDateTime;
// No datemode correction for intervals and for time-only values
if (fmt^.NumberFormat = nfTimeInterval) or (cell^.NumberValue < 1) then
cell^.DateTimeValue := cell^.NumberValue
else
case FDateMode of
dmODS1899: cell^.DateTimeValue := cell^.NumberValue + DATEMODE_1899_BASE;
dmODS1900: cell^.DateTimeValue := cell^.NumberValue + DATEMODE_1900_BASE;
dmODS1904: cell^.DateTimeValue := cell^.NumberValue + DATEMODE_1904_BASE;
end;
end;
end else
// (b) Date/time value
if (valueType = 'date') or (valueType = 'time') then
begin
valueStr := node.FirstChild.Nodevalue;
floatValue := ExtractDateTimeFromNode(ACellNode, fmt^.NumberFormat, fmt^.NumberFormatStr);
TsWorksheet(FWorkSheet).WriteDateTime(cell, floatValue);
end else
// (c) text
if (valueType = 'string') and (calcextValueType <> 'error') then
begin
node := ACellNode.FindNode('text:p');
if (node <> nil) and (node.FirstChild <> nil) then
begin
valueStr := node.FirstChild.Nodevalue;
TsWorksheet(FWorksheet).WriteText(cell, valueStr);
end;
end else
// (d) boolean
if (valuetype = 'boolean') then
begin
boolValue := ExtractBoolFromNode(ACellNode);
TsWorksheet(FWorksheet).WriteBoolValue(cell, boolValue);
end else
if (calcextValuetype = 'error') then
begin
if ExtractErrorFromNode(ACellNode, errorValue) then
TsWorksheet(FWorksheet).WriteErrorValue(cell, errorValue) else
TsWorksheet(FWorksheet).WriteText(cell, 'ERROR');
end else
// (e) Text
if (valueStr <> '') then
TsWorksheet(FWorksheet).WriteText(cell, valueStr);
end;
end else
// (d) boolean
if (valuetype = 'boolean') then
begin
boolValue := ExtractBoolFromNode(ACellNode);
TsWorksheet(FWorksheet).WriteBoolValue(cell, boolValue);
end else
if (calcextValuetype = 'error') then
begin
if ExtractErrorFromNode(ACellNode, errorValue) then
TsWorksheet(FWorksheet).WriteErrorValue(cell, errorValue) else
TsWorksheet(FWorksheet).WriteText(cell, 'ERROR');
end else
// (e) Text
if (valueStr <> '') then
TsWorksheet(FWorksheet).WriteText(cell, valueStr);
if hasFormula then TsWorkbook(FWorkbook).UnlockFormulas;
if FIsVirtualMode then
TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell);
if FIsVirtualMode then
TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell);
finally
TsWorkbook(FWorkbook).UnlockFormulas;
end;
end;
procedure TsSpreadOpenDocReader.ReadFromStream(AStream: TStream;