fpspreadsheet: Fix shared formulas incorrectly read from xlsx files. Fix formulas being lost when reading xlsx and ods files where formulas had not been calculated at saving.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4058 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-03-20 23:24:12 +00:00
parent 34ec877414
commit beca637d7c
4 changed files with 38 additions and 52 deletions

View File

@ -1275,6 +1275,7 @@ begin
ApplyStyleToCell(cell, stylename); ApplyStyleToCell(cell, stylename);
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex); fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
formula := '';
if (boReadFormulas in FWorkbook.Options) then if (boReadFormulas in FWorkbook.Options) then
begin begin
// Read formula, trim it, ... // Read formula, trim it, ...
@ -1352,6 +1353,7 @@ begin
FWorksheet.WriteBoolValue(cell, boolValue); FWorksheet.WriteBoolValue(cell, boolValue);
end else end else
// (e) Text // (e) Text
if (valueStr <> '') then
FWorksheet.WriteUTF8Text(cell, valueStr); FWorksheet.WriteUTF8Text(cell, valueStr);
if FIsVirtualMode then if FIsVirtualMode then
@ -4219,11 +4221,6 @@ begin
hyperlink := FWorksheet.FindHyperlink(ACell); hyperlink := FWorksheet.FindHyperlink(ACell);
SplitHyperlink(hyperlink^.Target, target, bookmark); SplitHyperlink(hyperlink^.Target, target, bookmark);
{
if (target = '') and (bookmark <> '') then
target := '#' + bookmark
else
}
if (target <> '') and (pos('file:', target) = 0) then if (target <> '') and (pos('file:', target) = 0) then
begin begin
u := ParseURI(target); u := ParseURI(target);
@ -4237,33 +4234,11 @@ begin
if (bookmark <> '') then if (bookmark <> '') then
target := target + '#' + bookmark; target := target + '#' + bookmark;
{
u := ParseURI(hyperlink^.Target);
if u.Protocol = '' then // relative file name, or internal link
begin
if target <> '' then target := '../' + target;
if bookmark <> '' then target := target + '#' + bookmark;
end else
target := hyperlink^.Target;
}
//ValidXMLText(target);
{
target := hyperlink^.Target;
if target[1] <> '#' then
begin
u := ParseURI(target);
if u.Protocol = '' then begin
//UriToFileName(hyperlink^.Target, target);
target := 'file:///' + ExpandFileName(target);
ValidXMLText(target);
// if not IsAbsoluteURI(target) then target := '..\' + target;
end;
end;
}
textp := Format( textp := Format(
'<text:p>'+ '<text:p>'+
'<text:a xlink:href="%s" xlink:type="simple">%s</text:a>'+ '<text:a xlink:href="%s" xlink:type="simple">%s</text:a>'+
'</text:p>', [target, txt]); '</text:p>', [target, txt]);
end else end else
textp := '<text:p>' + txt + '</text:p>'; textp := '<text:p>' + txt + '</text:p>';

View File

@ -1249,7 +1249,6 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorksheet.CalcFormulas; procedure TsWorksheet.CalcFormulas;
var var
node: TAVLTreeNode;
cell: PCell; cell: PCell;
begin begin
// prevent infinite loop due to triggering of formula calculation whenever // prevent infinite loop due to triggering of formula calculation whenever
@ -1257,23 +1256,15 @@ begin
inc(FWorkbook.FCalculationLock); inc(FWorkbook.FCalculationLock);
try try
// Step 1 - mark all formula cells as "not calculated" // Step 1 - mark all formula cells as "not calculated"
node := FCells.FindLowest; for cell in FCells do
while Assigned(node) do begin
cell := PCell(node.Data);
if HasFormula(cell) then if HasFormula(cell) then
SetCalcState(cell, csNotCalculated); SetCalcState(cell, csNotCalculated);
node := FCells.FindSuccessor(node);
end;
// Step 2 - calculate cells. If a not-yet-calculated cell is found it is // Step 2 - calculate cells. If a not-yet-calculated cell is found it is
// calculated and then marked as such. // calculated and then marked as such.
node := FCells.FindLowest; for cell in FCells do
while Assigned(node) do begin if HasFormula(cell) and (cell^.ContentType <> cctError) then
cell := PCell(node.Data);
if (cell^.ContentType <> cctError) and HasFormula(cell) then
CalcFormula(cell); CalcFormula(cell);
node := FCells.FindSuccessor(node);
end;
finally finally
dec(FWorkbook.FCalculationLock); dec(FWorkbook.FCalculationLock);

View File

@ -24,12 +24,14 @@ type
DeleteRow: Integer; DeleteRow: Integer;
Formula: String; Formula: String;
SollFormula: String; SollFormula: String;
{
SharedFormulaRowCount: Integer; // Size of shared formula block before insert/delete SharedFormulaRowCount: Integer; // Size of shared formula block before insert/delete
SharedFormulaColCount: Integer; SharedFormulaColCount: Integer;
SharedFormulaBaseCol_After: Integer; // Position of shared formula base after insert/delete SharedFormulaBaseCol_After: Integer; // Position of shared formula base after insert/delete
SharedFormulaBaseRow_After: Integer; SharedFormulaBaseRow_After: Integer;
SharedFormulaRowCount_After: Integer; // Size of shared formula block after insert/delete SharedFormulaRowCount_After: Integer; // Size of shared formula block after insert/delete
SharedFormulaColCount_After: Integer; SharedFormulaColCount_After: Integer;
}
MergedColCount: Integer; // size of merged block before insert/delete MergedColCount: Integer; // size of merged block before insert/delete
MergedRowCount: Integer; MergedRowCount: Integer;
MergedColCount_After: Integer; // size of merged block after insert/delete MergedColCount_After: Integer; // size of merged block after insert/delete
@ -237,12 +239,14 @@ begin
DeleteRow := -1; DeleteRow := -1;
Formula := ''; Formula := '';
SollFormula := ''; SollFormula := '';
{
SharedFormulaColCount := 0; SharedFormulaColCount := 0;
SharedFormulaRowCount := 0; SharedFormulaRowCount := 0;
SharedFormulaBaseCol_After := -1; SharedFormulaBaseCol_After := -1;
SharedFormulaBaseRow_After := -1; SharedFormulaBaseRow_After := -1;
SharedFormulaColCount_After := 0; SharedFormulaColCount_After := 0;
SharedFormulaRowCount_After := 0; SharedFormulaRowCount_After := 0;
}
MergedColCount := 0; MergedColCount := 0;
MergedRowCount := 0; MergedRowCount := 0;
end; end;
@ -931,16 +935,17 @@ var
r1,c1,r2,c2: Cardinal; r1,c1,r2,c2: Cardinal;
MyCell: PCell; MyCell: PCell;
TempFile: string; //write xls/xml to this file and read back from it TempFile: string; //write xls/xml to this file and read back from it
L, LL: TStringList; L: TStringList;
s: String; s: String;
expected: String; expected: String;
actual: String; actual: String;
expectedFormulas: array of array of String; // expectedFormulas: array of array of String;
begin begin
TempFile := GetTempFileName; TempFile := GetTempFileName;
L := TStringList.Create; L := TStringList.Create;
try try
{
// Extract soll formulas into a 2D array in case of shared formulas // Extract soll formulas into a 2D array in case of shared formulas
if (InsDelTestData[ATestIndex].SharedFormulaRowCount_After > 0) or if (InsDelTestData[ATestIndex].SharedFormulaRowCount_After > 0) or
(InsDelTestData[ATestIndex].SharedFormulaColCount_After > 0) then (InsDelTestData[ATestIndex].SharedFormulaColCount_After > 0) then
@ -963,7 +968,7 @@ begin
LL.Free; LL.Free;
end; end;
end; end;
}
L.Delimiter := '|'; L.Delimiter := '|';
L.StrictDelimiter := true; L.StrictDelimiter := true;
L.DelimitedText := InsDelTestData[ATestIndex].Layout; L.DelimitedText := InsDelTestData[ATestIndex].Layout;
@ -1047,6 +1052,7 @@ begin
end; end;
if HasFormula(MyCell) then if HasFormula(MyCell) then
begin begin
{
if (InsDelTestData[ATestIndex].SharedFormulaRowCount_After > 0) or if (InsDelTestData[ATestIndex].SharedFormulaRowCount_After > 0) or
(InsDelTestData[ATestIndex].SharedFormulaColCount_After > 0) (InsDelTestData[ATestIndex].SharedFormulaColCount_After > 0)
then then
@ -1057,6 +1063,7 @@ begin
'Shared formula mismatch, cell ' + CellNotation(MyWorksheet, Row, Col) 'Shared formula mismatch, cell ' + CellNotation(MyWorksheet, Row, Col)
) )
else else
}
CheckEquals( CheckEquals(
InsDelTestData[ATestIndex].SollFormula, InsDelTestData[ATestIndex].SollFormula,
MyWorksheet.ReadFormulaAsString(MyCell), MyWorksheet.ReadFormulaAsString(MyCell),

View File

@ -66,6 +66,7 @@ type
FFillList: TFPList; FFillList: TFPList;
FBorderList: TFPList; FBorderList: TFPList;
FHyperlinkList: TFPList; FHyperlinkList: TFPList;
FSharedFormulaBaseList: TFPList;
FThemeColors: array of TsColorValue; FThemeColors: array of TsColorValue;
FWrittenByFPS: Boolean; FWrittenByFPS: Boolean;
procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer); procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
@ -468,6 +469,7 @@ begin
FHyperlinkList := TFPList.Create; FHyperlinkList := TFPList.Create;
FCellFormatList := TsCellFormatList.Create(true); FCellFormatList := TsCellFormatList.Create(true);
// Allow duplicates because xf indexes used in cell records cannot be found any more. // Allow duplicates because xf indexes used in cell records cannot be found any more.
FSharedFormulaBaseList := TFPList.Create;
FPointSeparatorSettings := DefaultFormatSettings; FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.'; FPointSeparatorSettings.DecimalSeparator := '.';
@ -487,6 +489,7 @@ begin
FHyperlinkList.Free; FHyperlinkList.Free;
FSharedStrings.Free; FSharedStrings.Free;
FSharedFormulaBaseList.Free; // Don't free items, they are worksheet cells
// FCellFormatList and FFontList are destroyed by ancestor // FCellFormatList and FFontList are destroyed by ancestor
@ -664,7 +667,7 @@ procedure TsSpreadOOXMLReader.ReadCell(ANode: TDOMNode; AWorksheet: TsWorksheet)
var var
addr, s: String; addr, s: String;
rowIndex, colIndex: Cardinal; rowIndex, colIndex: Cardinal;
cell: PCell; cell, sharedformulabase: PCell;
datanode: TDOMNode; datanode: TDOMNode;
dataStr: String; dataStr: String;
formulaStr: String; formulaStr: String;
@ -716,13 +719,21 @@ begin
begin begin
// Shared formula // Shared formula
s := GetAttrValue(datanode, 'ref'); s := GetAttrValue(datanode, 'ref');
if (s <> '') then // This is the shared formula range if (s <> '') then // This defines the shared formula range
begin begin
// Split shared formula into single-cell formulas AWorksheet.WriteFormula(cell, FormulaStr);
ParseCellRangeString(s, rng); // We store the shared formula base in the SharedFormulaBaseList.
for r := rng.Row1 to rng.Row2 do // The list index is identical with the 'si' attribute of the node.
for c := rng.Col1 to rng.Col2 do FSharedFormulaBaseList.Add(cell);
FWorksheet.CopyFormula(cell, r, c); end else
begin
// Get index into the SharedFormulaBaseList
s := GetAttrValue(datanode, 'si');
if s <> '' then
begin
sharedformulabase := PCell(FSharedFormulaBaseList[StrToInt(s)]);
FWorksheet.CopyFormula(sharedformulabase, rowindex, colindex);
end;
end; end;
end end
else else
@ -735,8 +746,10 @@ begin
// get data type // get data type
s := GetAttrValue(ANode, 't'); // "t" = data type s := GetAttrValue(ANode, 't'); // "t" = data type
if (s = '') and (dataStr = '') then if (s = '') and (dataStr = '') then
AWorksheet.WriteBlank(cell) begin
else AWorksheet.WriteBlank(cell); // this erases the formula!!!
if formulaStr <> '' then cell^.FormulaValue := formulaStr;
end else
if (s = '') or (s = 'n') then begin if (s = '') or (s = 'n') then begin
// Number or date/time, depending on format // Number or date/time, depending on format
number := StrToFloat(dataStr, FPointSeparatorSettings); number := StrToFloat(dataStr, FPointSeparatorSettings);