You've already forked lazarus-ccr
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:
@ -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>';
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
Reference in New Issue
Block a user