You've already forked lazarus-ccr
fpspreadsheet: Write single-sheet 3d references (e.g., 'Sheet1!A1:B6') to BIFF8 (xls). Issues with reading.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6405 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@@ -87,6 +87,7 @@ type
|
||||
|
||||
TsExpressionResult = record
|
||||
Worksheet : TsWorksheet;
|
||||
Worksheet2 : TsWorksheet;
|
||||
ResString : String;
|
||||
case ResultType : TsResultType of
|
||||
rtEmpty : ();
|
||||
@@ -114,6 +115,7 @@ type
|
||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; virtual; abstract;
|
||||
function AsString: string; virtual; abstract;
|
||||
procedure Check; virtual; //abstract;
|
||||
function Has3DLink: Boolean; virtual;
|
||||
function NodeType: TsResultType; virtual; abstract;
|
||||
function NodeValue: TsExpressionResult;
|
||||
property Parser: TsExpressionParser read FParser;
|
||||
@@ -131,6 +133,7 @@ type
|
||||
public
|
||||
constructor Create(AParser: TsExpressionParser; ALeft, ARight: TsExprNode);
|
||||
destructor Destroy; override;
|
||||
function Has3DLink: Boolean; override;
|
||||
property Left: TsExprNode read FLeft;
|
||||
property Right: TsExprNode read FRight;
|
||||
end;
|
||||
@@ -551,6 +554,7 @@ type
|
||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
||||
function AsString: String; override;
|
||||
procedure Check; override;
|
||||
function Has3DLink: Boolean; override;
|
||||
property ArgumentNodes: TsExprArgumentArray read FArgumentNodes;
|
||||
property ArgumentParams: TsExprParameterArray read FArgumentParams;
|
||||
end;
|
||||
@@ -579,16 +583,6 @@ type
|
||||
property CallBack: TsExprFunctionEvent read FCallBack;
|
||||
end;
|
||||
|
||||
TsSheetNameExprNode = class(TsExprNode)
|
||||
private
|
||||
FSheetName: String;
|
||||
public
|
||||
constructor Create(AParser: TsExpressionParser; ASheetName: String);
|
||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
||||
function AsString: string; override;
|
||||
property SheetName: String read FSheetName;
|
||||
end;
|
||||
|
||||
{ TsCellExprNode }
|
||||
TsCellExprNode = class(TsExprNode)
|
||||
private
|
||||
@@ -611,45 +605,40 @@ type
|
||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
||||
function AsString: string; override;
|
||||
procedure Check; override;
|
||||
function Has3DLink: Boolean; override;
|
||||
function NodeType: TsResultType; override;
|
||||
property Worksheet: TsWorksheet read FWorksheet;
|
||||
end;
|
||||
|
||||
(*
|
||||
{ TsSheetCellExprNode }
|
||||
TsSheetCellExprNode = class(TsBasicCellExprNode)
|
||||
protected
|
||||
function GetSheetIndex: Integer;
|
||||
public
|
||||
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
|
||||
ACellString: String); overload;
|
||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
||||
function AsString: string; override;
|
||||
end; *)
|
||||
|
||||
|
||||
{ TsCellRangeExprNode }
|
||||
|
||||
TsCellRangeIndex = 1..2;
|
||||
|
||||
TsCellRangeExprNode = class(TsExprNode)
|
||||
private
|
||||
FWorksheet: TsWorksheet;
|
||||
FWorksheet2: TsWorksheet;
|
||||
FRow: array[TsCellRangeIndex] of Cardinal;
|
||||
FCol: array[TsCellRangeIndex] of Cardinal;
|
||||
FFlags: TsRelFlags;
|
||||
FonOtherSheet: Boolean;
|
||||
protected
|
||||
function GetCol(AIndex: TsCellRangeIndex): Cardinal;
|
||||
function GetRow(AIndex: TsCellRangeIndex): Cardinal;
|
||||
procedure GetNodeValue(out Result: TsExpressionResult); override;
|
||||
function GetSheetIndex(AIndex: TscellRangeIndex): Integer;
|
||||
public
|
||||
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
|
||||
ACellRangeString: String); overload;
|
||||
ACellRangeString: String; OnOtherSheet: Boolean); overload;
|
||||
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
|
||||
ARow1,ACol1, ARow2,ACol2: Cardinal; AFlags: TsRelFlags; OnOtherSheet: Boolean); overload;
|
||||
constructor Create(AParser: TsExpressionParser; AWorksheet1, AWorksheet2: TsWorksheet;
|
||||
ACellRangeString: String); overload;
|
||||
constructor Create(AParser: TsExpressionParser; AWorksheet1, AWorksheet2: TsWorksheet;
|
||||
ARow1,ACol1, ARow2,ACol2: Cardinal; AFlags: TsRelFlags); overload;
|
||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
||||
function AsString: String; override;
|
||||
procedure Check; override;
|
||||
function Has3DLink: Boolean; override;
|
||||
function NodeType: TsResultType; override;
|
||||
property Worksheet: TsWorksheet read FWorksheet;
|
||||
end;
|
||||
@@ -763,6 +752,7 @@ type
|
||||
function CopyMode: Boolean;
|
||||
function Evaluate: TsExpressionResult;
|
||||
procedure EvaluateExpression(out Result: TsExpressionResult);
|
||||
function Has3DLinks: Boolean;
|
||||
procedure PrepareCopyMode(ASourceCell, ADestCell: PCell);
|
||||
function ResultType: TsResultType;
|
||||
|
||||
@@ -1681,6 +1671,8 @@ begin
|
||||
Result := TsConstExprNode.CreateString(self, CurrentToken)
|
||||
else if (TokenType = ttCell) then
|
||||
Result := TsCellExprNode.Create(self, FWorksheet, CurrentToken, false)
|
||||
else if (TokenType = ttCellRange) then
|
||||
Result := TsCellRangeExprNode.Create(self, FWorksheet, CurrentToken, false)
|
||||
else if (TokenType = ttSheetName) then begin
|
||||
sheetName := CurrentToken;
|
||||
GetToken;
|
||||
@@ -1694,11 +1686,9 @@ begin
|
||||
sheet := FWorksheet.WorkBook.GetWorksheetByName(sheetName);
|
||||
if sheet = nil then
|
||||
sheet := FWorksheet.Workbook.AddWorksheet(sheetName, true);
|
||||
Result := TsCellRangeExprNode.Create(self, sheet, CurrentToken);
|
||||
Result := TsCellRangeExprNode.Create(self, sheet, sheet, CurrentToken);
|
||||
end;
|
||||
end
|
||||
else if (TokenType = ttCellRange) then
|
||||
Result := TsCellRangeExprNode.Create(self, FWorksheet, CurrentToken)
|
||||
else if (TokenType = ttError) then
|
||||
Result := TsConstExprNode.CreateError(self, CurrentToken)
|
||||
else if not (TokenType in [ttIdentifier]) then
|
||||
@@ -1821,6 +1811,11 @@ begin
|
||||
Result := BuildStringFormula(AFormatSettings);
|
||||
end;
|
||||
|
||||
function TsExpressionParser.Has3DLinks: Boolean;
|
||||
begin
|
||||
Result := FExprNode.Has3DLink;
|
||||
end;
|
||||
|
||||
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
|
||||
begin
|
||||
if FDialect = AValue then exit;
|
||||
@@ -1887,7 +1882,7 @@ procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula);
|
||||
ID: TsExprIdentifierDef;
|
||||
i, n: Integer;
|
||||
args: TsExprArgumentArray;
|
||||
sheet: TsWorksheet;
|
||||
sheet, sheet2: TsWorksheet;
|
||||
begin
|
||||
if AIndex < 0 then
|
||||
exit;
|
||||
@@ -1930,7 +1925,16 @@ procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula);
|
||||
r2 := AFormula[AIndex].Row2;
|
||||
c2 := AFormula[AIndex].Col2;
|
||||
flags := AFormula[AIndex].RelFlags;
|
||||
ANode := TsCellRangeExprNode.Create(self, FWorksheet, r, c, r2, c2, flags);
|
||||
idx := AFormula[AIndex].Sheet;
|
||||
if idx = -1 then
|
||||
ANode := TsCellRangeExprNode.Create(self, FWorksheet, r, c, r2, c2, flags, false)
|
||||
else begin
|
||||
sheet := FWorksheet.Workbook.GetWorksheetByIndex(idx);
|
||||
idx := AFormula[AIndex].Sheet2;
|
||||
if idx = -1 then sheet2 := sheet
|
||||
else sheet2 := FWorksheet.Workbook.GetWorksheetByIndex(idx);
|
||||
ANode := TsCellRangeExprNode.Create(self, sheet, sheet2, r,c, r2,c2, flags);
|
||||
end;
|
||||
dec(AIndex);
|
||||
end;
|
||||
fekNum:
|
||||
@@ -2588,6 +2592,11 @@ begin
|
||||
Result := false;
|
||||
end;
|
||||
|
||||
function TsExprNode.Has3DLink: Boolean;
|
||||
begin
|
||||
Result := false;
|
||||
end;
|
||||
|
||||
function TsExprNode.NodeValue: TsExpressionResult;
|
||||
begin
|
||||
GetNodeValue(Result);
|
||||
@@ -2633,6 +2642,11 @@ begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TsBinaryOperationExprNode.Has3DLink: Boolean;
|
||||
begin
|
||||
Result := FLeft.Has3DLink or FRight.Has3DLink;
|
||||
end;
|
||||
|
||||
function TsBinaryOperationExprNode.HasError(out AResult: TsExpressionResult): Boolean;
|
||||
begin
|
||||
Result := Left.HasError(AResult) or Right.HasError(AResult);
|
||||
@@ -3575,6 +3589,15 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TsFunctionExprNode.Has3DLink: Boolean;
|
||||
var
|
||||
i : Integer;
|
||||
begin
|
||||
for i := 0 to Length(FArgumentParams)-1 do
|
||||
if FArgumentNodes[i].Has3DLink then exit(true);
|
||||
Result := false;
|
||||
end;
|
||||
|
||||
|
||||
{ TsFunctionCallBackExprNode }
|
||||
|
||||
@@ -3611,7 +3634,7 @@ begin
|
||||
FCallBack(Result, FArgumentParams);
|
||||
end;
|
||||
|
||||
|
||||
(*
|
||||
{ TsSheetNameExprNode }
|
||||
constructor TsSheetNameExprNode.Create(AParser: TsExpressionParser;
|
||||
ASheetName: string);
|
||||
@@ -3629,7 +3652,7 @@ function TsSheetnameExprNode.AsString: string;
|
||||
begin
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
*)
|
||||
|
||||
{ TsCellExprNode }
|
||||
|
||||
@@ -3761,6 +3784,11 @@ begin
|
||||
Result := book.GetWorksheetIndex(FWorksheet);
|
||||
end;
|
||||
|
||||
function TsCellExprNode.Has3DLink: Boolean;
|
||||
begin
|
||||
Result := FOtherSheet;
|
||||
end;
|
||||
|
||||
function TsCellExprNode.NodeType: TsResultType;
|
||||
begin
|
||||
Result := rtCell;
|
||||
@@ -3836,7 +3864,7 @@ end;
|
||||
{ TsCellRangeExprNode }
|
||||
|
||||
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
|
||||
AWorksheet: TsWorksheet; ACellRangeString: String);
|
||||
AWorksheet: TsWorksheet; ACellRangeString: String; OnOtherSheet: Boolean);
|
||||
var
|
||||
r1, c1, r2, c2: Cardinal;
|
||||
flags: TsRelFlags;
|
||||
@@ -3846,16 +3874,17 @@ begin
|
||||
ParseCellString(ACellRangeString, r1, c1, flags);
|
||||
if rfRelRow in flags then Include(flags, rfRelRow2);
|
||||
if rfRelCol in flags then Include(flags, rfRelCol2);
|
||||
Create(AParser, AWorksheet, r1, c1, r1, c1, flags);
|
||||
Create(AParser, AWorksheet, r1, c1, r1, c1, flags, OnOtherSheet);
|
||||
end else
|
||||
begin
|
||||
ParseCellRangeString(ACellRangeString, r1, c1, r2, c2, flags);
|
||||
Create(AParser, AWorksheet, r1, c1, r2, c2, flags);
|
||||
Create(AParser, AWorksheet, r1, c1, r2, c2, flags, OnOtherSheet);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
|
||||
AWorksheet: TsWorksheet; ARow1,ACol1,ARow2,ACol2: Cardinal; AFlags: TsRelFlags);
|
||||
AWorksheet: TsWorksheet; ARow1,ACol1,ARow2,ACol2: Cardinal;
|
||||
AFlags: TsRelFlags; OnOtherSheet: Boolean);
|
||||
begin
|
||||
FParser := AParser;
|
||||
FWorksheet := AWorksheet;
|
||||
@@ -3864,27 +3893,73 @@ begin
|
||||
FRow[2] := ARow2;
|
||||
FCol[2] := ACol2;
|
||||
FFlags := AFlags;
|
||||
FOnOtherSheet := OnOtherSheet;
|
||||
end;
|
||||
|
||||
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
|
||||
AWorksheet1, AWorksheet2: TsWorksheet; ACellRangeString: String);
|
||||
var
|
||||
r1, c1, r2, c2: Cardinal;
|
||||
flags: TsRelFlags;
|
||||
begin
|
||||
if pos(':', ACellRangeString) = 0 then
|
||||
begin
|
||||
ParseCellString(ACellRangeString, r1, c1, flags);
|
||||
if rfRelRow in flags then Include(flags, rfRelRow2);
|
||||
if rfRelCol in flags then Include(flags, rfRelCol2);
|
||||
Create(AParser, AWorksheet1, AWorksheet2, r1, c1, r1, c1, flags);
|
||||
end else
|
||||
begin
|
||||
ParseCellRangeString(ACellRangeString, r1, c1, r2, c2, flags);
|
||||
Create(AParser, AWorksheet1, AWorksheet2, r1, c1, r2, c2, flags);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
|
||||
AWorksheet1, AWorksheet2: TsWorksheet; ARow1,ACol1,ARow2,ACol2: Cardinal;
|
||||
AFlags: TsRelFlags);
|
||||
begin
|
||||
FParser := AParser;
|
||||
FWorksheet := AWorksheet1;
|
||||
FWorksheet2 := AWorksheet2;
|
||||
FRow[1] := ARow1;
|
||||
FCol[1] := ACol1;
|
||||
FRow[2] := ARow2;
|
||||
FCol[2] := ACol2;
|
||||
FFlags := AFlags;
|
||||
FOnOtherSheet := true;
|
||||
end;
|
||||
|
||||
function TsCellRangeExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
|
||||
begin
|
||||
Result := RPNCellRange(GetRow(1), GetCol(1), GetRow(2), GetCol(2), FFlags, ANext);
|
||||
if FOnOtherSheet then
|
||||
Result := RPNCellRange3D(
|
||||
GetSheetIndex(1), GetRow(1), GetCol(1),
|
||||
GetSheetIndex(2), GetRow(2), GetCol(2),
|
||||
FFlags, ANext
|
||||
)
|
||||
else
|
||||
Result := RPNCellRange(GetRow(1), GetCol(1), GetRow(2), GetCol(2), FFlags, ANext);
|
||||
end;
|
||||
|
||||
function TsCellRangeExprNode.AsString: string;
|
||||
var
|
||||
r, c: Array[TsCellRangeIndex] of Cardinal;
|
||||
i: TsCellRangeIndex;
|
||||
r1, c1, r2, c2: Cardinal;
|
||||
s1, s2: String;
|
||||
begin
|
||||
for i in TsCellRangeIndex do
|
||||
begin
|
||||
r[i] := GetRow(i);
|
||||
c[i] := GetCol(i);
|
||||
r1 := GetRow(1); r2 := GetRow(2);
|
||||
c1 := GetCol(1); c2 := GetCol(2);
|
||||
s1 := FWorksheet.Name; s2 := FWorksheet2.Name;
|
||||
|
||||
case FParser.Dialect of
|
||||
fdExcelA1:
|
||||
Result := GetCellRangeString(s1, s2, r1, c1, r2, c2, FFlags, true);
|
||||
fdExcelR1C1:
|
||||
Result := GetCellRangeString_R1C1(s1, s2, r1, c1, r2, c2, FFlags,
|
||||
FParser.FSourceCell^.Row, FParser.FSourceCell^.Col);
|
||||
fdOpenDocument:
|
||||
Result := GetCellRangeString_ODS(s1, s2, r1, c1, r2, c2, FFlags);
|
||||
end;
|
||||
if (r[1] = r[2]) and (c[1] = c[2]) then
|
||||
Result := GetCellString(r[1], r[1], FFlags)
|
||||
else
|
||||
Result := GetCellRangeString(r[1], c[1], r[2], c[2], FFlags);
|
||||
end;
|
||||
|
||||
procedure TsCellRangeExprNode.Check;
|
||||
@@ -3911,28 +3986,38 @@ end;
|
||||
|
||||
procedure TsCellRangeExprNode.GetNodeValue(out Result: TsExpressionResult);
|
||||
var
|
||||
r, c: Array[TsCellRangeIndex] of Cardinal;
|
||||
rr, cc: Cardinal;
|
||||
r, c, s: Array[TsCellRangeIndex] of Integer;
|
||||
rr, cc, ss: Integer;
|
||||
i: TsCellRangeIndex;
|
||||
cell: PCell;
|
||||
book: TsWorkbook;
|
||||
sheet: TsWorksheet;
|
||||
begin
|
||||
for i in TsCellRangeIndex do
|
||||
begin
|
||||
r[i] := GetRow(i);
|
||||
c[i] := GetCol(i);
|
||||
s[i] := GetSheetIndex(i);
|
||||
end;
|
||||
if not FOnOtherSheet then s[2] := s[1];
|
||||
|
||||
book := FWorksheet.Workbook;
|
||||
|
||||
for ss := s[1] to s[2] do begin
|
||||
sheet := book.GetWorksheetByIndex(ss);
|
||||
for rr := r[1] to r[2] do
|
||||
for cc := c[1] to c[2] do
|
||||
begin
|
||||
cell := sheet.FindCell(rr, cc);
|
||||
if HasFormula(cell) then
|
||||
case sheet.GetCalcState(cell) of
|
||||
csNotCalculated:
|
||||
sheet.CalcFormula(cell);
|
||||
csCalculating:
|
||||
raise ECalcEngine.Create(rsCircularReference);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
for rr := r[1] to r[2] do
|
||||
for cc := c[1] to c[2] do
|
||||
begin
|
||||
cell := FWorksheet.FindCell(rr, cc);
|
||||
if HasFormula(cell) then
|
||||
case FWorksheet.GetCalcState(cell) of
|
||||
csNotCalculated:
|
||||
FWorksheet.CalcFormula(cell);
|
||||
csCalculating:
|
||||
raise ECalcEngine.Create(rsCircularReference);
|
||||
end;
|
||||
end;
|
||||
|
||||
Result.ResultType := rtCellRange;
|
||||
Result.ResCellRange.Row1 := r[1];
|
||||
@@ -3940,6 +4025,7 @@ begin
|
||||
Result.ResCellRange.Row2 := r[2];
|
||||
Result.ResCellRange.Col2 := c[2];
|
||||
Result.Worksheet := FWorksheet;
|
||||
Result.Worksheet2 := FWorksheet2;
|
||||
end;
|
||||
|
||||
function TsCellRangeExprNode.GetRow(AIndex: TsCellRangeIndex): Cardinal;
|
||||
@@ -3949,6 +4035,24 @@ begin
|
||||
Result := FRow[AIndex] - FParser.FSourceCell^.Row + FParser.FDestCell^.Row;
|
||||
end;
|
||||
|
||||
function TsCellRangeExprNode.GetSheetIndex(AIndex: TsCellRangeIndex): Integer;
|
||||
var
|
||||
book: TsWorkbook;
|
||||
sheet: TsWorksheet;
|
||||
begin
|
||||
case AIndex of
|
||||
1: sheet := FWorksheet;
|
||||
2: sheet := FWorksheet2;
|
||||
end;
|
||||
book := sheet.Workbook;
|
||||
Result := book.GetWorksheetIndex(sheet);
|
||||
end;
|
||||
|
||||
function TsCellRangeExprNode.Has3DLink: Boolean;
|
||||
begin
|
||||
Result := FOnOtherSheet;
|
||||
end;
|
||||
|
||||
function TsCellRangeExprNode.NodeType: TsResultType;
|
||||
begin
|
||||
Result := rtCellRange;
|
||||
|
@@ -803,7 +803,8 @@ type
|
||||
function GetWorksheetByIndex(AIndex: Integer): TsWorksheet;
|
||||
function GetWorksheetByName(AName: String): TsWorksheet;
|
||||
function GetWorksheetCount: Integer;
|
||||
function GetWorksheetIndex(AWorksheet: TsWorksheet): Integer;
|
||||
function GetWorksheetIndex(AWorksheet: TsWorksheet): Integer; overload;
|
||||
function GetWorksheetIndex(const AWorksheetName: String): Integer; overload;
|
||||
procedure RemoveAllWorksheets;
|
||||
procedure RemoveAllEmptyWorksheets;
|
||||
procedure RemoveWorksheet(AWorksheet: TsWorksheet);
|
||||
@@ -4180,6 +4181,7 @@ begin
|
||||
Result := soProtected in FOptions;
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Setter for the worksheet name property. Checks if the name is valid, and
|
||||
exits without any change if not. Creates an event OnChangeWorksheet.
|
||||
@@ -5727,15 +5729,19 @@ begin
|
||||
if (AFormula <> '') and (AFormula[1] = '=') then
|
||||
AFormula := Copy(AFormula, 2, Length(AFormula));
|
||||
|
||||
// Convert "localized" formula to standard format
|
||||
if ALocalized then begin
|
||||
parser := TsSpreadsheetParser.Create(self);
|
||||
try
|
||||
parser := TsSpreadsheetParser.Create(self);
|
||||
try
|
||||
if ALocalized then begin
|
||||
// Convert "localized" formula to standard format
|
||||
parser.LocalizedExpression[Workbook.FormatSettings] := AFormula;
|
||||
AFormula := parser.Expression;
|
||||
finally
|
||||
parser.Free;
|
||||
end;
|
||||
end else
|
||||
parser.Expression := AFormula;
|
||||
if parser.Has3DLinks
|
||||
then ACell.Flags := ACell.Flags + [cf3dFormula]
|
||||
else ACell.Flags := ACell.Flags - [cf3dFormula];
|
||||
finally
|
||||
parser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -9001,6 +9007,18 @@ begin
|
||||
Result := FWorksheets.IndexOf(AWorksheet);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Returns the index of the worksheet having the specified name, or -1 if the
|
||||
worksheet does not exist.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbook.GetWorksheetIndex(const AWorksheetName: String): Integer;
|
||||
begin
|
||||
for Result := 0 to FWorksheets.Count-1 do
|
||||
if TsWorksheet(FWorksheets[Result]).Name = AWorksheetName then
|
||||
exit;
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Clears the list of Worksheets and releases their memory.
|
||||
|
||||
|
@@ -87,6 +87,7 @@ function NewRPNItem: PRPNItem;
|
||||
begin
|
||||
New(Result);
|
||||
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
||||
Result^.FE.Sheet2 := -1;
|
||||
Result^.FE.StringValue := '';
|
||||
Result^.FE.SheetNames := '';
|
||||
end;
|
||||
|
@@ -585,7 +585,8 @@ type
|
||||
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
|
||||
|
||||
{@@ Cell flag }
|
||||
TsCellFlag = (cfCalculating, cfCalculated, cfHasComment, cfHyperlink, cfMerged);
|
||||
TsCellFlag = (cfCalculating, cfCalculated, cfHasComment, cfHyperlink, cfMerged,
|
||||
cf3dFormula);
|
||||
|
||||
{@@ Set of cell flags }
|
||||
TsCellFlags = set of TsCellFlag;
|
||||
|
@@ -81,6 +81,8 @@ function ParseSheetCellString(const AStr: String; out ASheetName: String;
|
||||
function ParseCellRowString(const AStr: string; out ARow: Cardinal): Boolean;
|
||||
function ParseCellColString(const AStr: string; out ACol: Cardinal): Boolean;
|
||||
|
||||
function GetCellRangeString(ASheet1, ASheet2: String; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String; overload;
|
||||
function GetCellRangeString(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String; overload;
|
||||
function GetCellRangeString(ARange: TsCellRange;
|
||||
@@ -99,7 +101,21 @@ function ParseCellString_R1C1(const AStr: string; ABaseRow, ABaseCol: Cardinal;
|
||||
out ACellRow, ACellCol: Cardinal): Boolean; overload;
|
||||
|
||||
function GetCellString_R1C1(ARow, ACol: Cardinal; AFlags: TsRelFlags = [rfRelRow, rfRelCol];
|
||||
ARefRow: Cardinal = Cardinal(-1); ARefCol: Cardinal = Cardinal(-1)): String;
|
||||
ARefRow: Cardinal = Cardinal(-1); ARefCol: Cardinal = Cardinal(-1)): String; overload;
|
||||
function GetCellRangeString_R1C1(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = [rfRelRow, rfRelCol];
|
||||
ARefRow: Cardinal = Cardinal(-1); ARefCol: Cardinal = Cardinal(-1)): String; overload;
|
||||
function GetCellRangeString_R1C1(ASheet1, ASheet2: String;
|
||||
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = [rfRelRow, rfRelCol];
|
||||
ARefRow: Cardinal = Cardinal(-1); ARefCol: Cardinal = Cardinal(-1)): String; overload;
|
||||
|
||||
// OpenDocument Syntax
|
||||
function GetCellRangeString_ODS(ASheet1, ASheet2: String; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = rfAllRel): String; overload;
|
||||
function GetCellRangeString_ODS(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = rfAllRel): String; overload;
|
||||
function GetCellRangeString_ODS(ARange: TsCellRange;
|
||||
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String; overload;
|
||||
|
||||
|
||||
// Error strings
|
||||
@@ -985,6 +1001,36 @@ begin
|
||||
Result := Result + 'C' + IntToStr(LongInt(ACol)+1);
|
||||
end;
|
||||
|
||||
function GetCellRangeString_R1C1(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = [rfRelRow, rfRelCol];
|
||||
ARefRow: Cardinal = Cardinal(-1); ARefCol: Cardinal = Cardinal(-1)): String;
|
||||
var
|
||||
s1, s2: String;
|
||||
begin
|
||||
s1 := GetCellString_R1C1(ARow1, ACol1, AFlags, ARefRow, ARefCol);
|
||||
s2 := GetCellString_R1C1(ARow2, ACol2, AFlags, ARefRow, ARefCol);
|
||||
if s1 = s2 then
|
||||
Result := s1
|
||||
else
|
||||
Result := Format('%s:%s', [s1, s2]);
|
||||
end;
|
||||
|
||||
function GetCellRangeString_R1C1(ASheet1, ASheet2: String;
|
||||
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = [rfRelRow, rfRelCol];
|
||||
ARefRow: Cardinal = Cardinal(-1); ARefCol: Cardinal = Cardinal(-1)): String;
|
||||
var
|
||||
s: String;
|
||||
begin
|
||||
s := GetCellRangeString_R1C1(ARow1, ACol1, ARow2, ACol2, AFlags, ARefRow, ARefCol);
|
||||
if (ASheet1 = '') and (ASheet2 = '') then
|
||||
Result := s
|
||||
else if (ASheet2 = '') or (ASheet1 = ASheet2) then
|
||||
Result := Format('%s!%s', [ASheet1, s])
|
||||
else
|
||||
Result := Format('%s:%s!%s', [ASheet1, ASheet2, s]);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Calculates a cell range address string from zero-based column and row indexes
|
||||
@@ -1019,6 +1065,23 @@ begin
|
||||
]);
|
||||
end;
|
||||
|
||||
function GetCellRangeString(ASheet1, ASheet2: String; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String;
|
||||
var
|
||||
s: String;
|
||||
begin
|
||||
s := GetCellRangeString(ARow1, ACol1, ARow2, ACol2, AFlags, Compact);
|
||||
if (ASheet1 = '') and (ASheet2 = '') then
|
||||
Result := s
|
||||
else if ASheet2 = '' then
|
||||
Result := Format('%s!%s', [ASheet1, s])
|
||||
else if Compact and (ASheet1 = ASheet2) then
|
||||
Result := Format('%s!%s', [ASheet1, s])
|
||||
else
|
||||
Result := Format('%s:%s!%s', [ASheet1, ASheet2, s]);
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Calculates a cell range address string from a TsCellRange record
|
||||
and the relative address state flags.
|
||||
@@ -1041,6 +1104,53 @@ begin
|
||||
AFlags, Compact);
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Calculates a cell range string with sheet specification in OpenDocument syntax
|
||||
-------------------------------------------------------------------------------}
|
||||
function GetCellRangeString_ODS(ASheet1, ASheet2: String;
|
||||
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = rfAllRel): String;
|
||||
var
|
||||
s1, s2: String;
|
||||
begin
|
||||
s1 := Format('%s%s%s%s', [
|
||||
RELCHAR[rfRelCol in AFlags], GetColString(ACol1),
|
||||
RELCHAR[rfRelRow in AFlags], ARow1 + 1
|
||||
]);
|
||||
s2 := Format('%s%s%s%s', [
|
||||
RELCHAR[rfRelCol2 in AFlags], GetColString(ACol2),
|
||||
RELCHAR[rfRelRow2 in AFlags], ARow2 + 1
|
||||
]);
|
||||
|
||||
if (ASheet1 = '') and (ASheet2 = '') then
|
||||
begin
|
||||
if s1 = s2 then
|
||||
Result := s1
|
||||
else
|
||||
Result := Format('%s:%s', [s1, s2])
|
||||
end else
|
||||
if (ASheet2 = '') or (ASheet1 = ASheet2) then begin
|
||||
if s1 = s2 then
|
||||
Result := Format('%s.%s', [ASheet1, s1])
|
||||
else
|
||||
Result := Format('%s.%s:.%s', [ASheet1, s1, s2]); // Sheet1.A1:.B2
|
||||
end else
|
||||
Result := Format('%s.%s:%s.%s', [ASheet1, s1, ASheet2, s2]); // Sheet.A1:Sheet2.B2
|
||||
end;
|
||||
|
||||
function GetCellRangeString_ODS(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags = rfAllRel): String;
|
||||
begin
|
||||
Result := GetCellRangeString(ARow1, ACol1, ARow2, ACol2, AFlags, true);
|
||||
end;
|
||||
|
||||
function GetCellRangeString_ODS(ARange: TsCellRange;
|
||||
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String;
|
||||
begin
|
||||
Result := GetCellRangeString(ARange, AFlags, true);
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Returns the error value code from a string. Result is false, if the string does
|
||||
not match one of the predefined error strings.
|
||||
|
@@ -79,8 +79,9 @@ type
|
||||
procedure ReadFONT(const AStream: TStream);
|
||||
procedure ReadFORMAT(AStream: TStream); override;
|
||||
procedure ReadLABEL(AStream: TStream); override;
|
||||
function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; override;
|
||||
procedure ReadRPNSheetIndex(AStream: TStream; out ASheet1, ASheet2: Integer); override;
|
||||
// function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; override;
|
||||
procedure ReadRPNSheetIndex(AStream: TStream; out ADocumentURL: String;
|
||||
out ASheet1, ASheet2: Integer); override;
|
||||
procedure ReadRSTRING(AStream: TStream);
|
||||
procedure ReadStandardWidth(AStream: TStream; ASheet: TsWorksheet);
|
||||
procedure ReadStringRecord(AStream: TStream); override;
|
||||
@@ -115,9 +116,13 @@ type
|
||||
procedure WriteIndex(AStream: TStream);
|
||||
procedure WriteLABEL(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AValue: string; ACell: PCell); override;
|
||||
procedure WriteLocalLinkTable(AStream: TStream);
|
||||
procedure WriteLocalLinkTable(AStream: TStream; AWorksheet: TsWorksheet);
|
||||
(*
|
||||
function WriteRPNCellAddress3D(AStream: TStream; ASheet, ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): Word; override;
|
||||
*)
|
||||
function WriteRPNSheetIndex(AStream: TStream; ADocumentURL: String;
|
||||
ASheet1, ASheet2: Integer): Word; override;
|
||||
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
||||
procedure WriteStyle(AStream: TStream);
|
||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||
@@ -608,7 +613,7 @@ begin
|
||||
FixCols(FWorksheet);
|
||||
FixRows(FWorksheet);
|
||||
end;
|
||||
|
||||
{
|
||||
function TsSpreadBIFF5Reader.ReadRPNCellRange3D(AStream: TStream;
|
||||
var ARPNItem: PRPNItem): Boolean;
|
||||
var
|
||||
@@ -639,34 +644,54 @@ begin
|
||||
if r2 = $FFFF then r2 := Cardinal(-1);
|
||||
if c2 = $FF then c2 := Cardinal(-1);
|
||||
ARPNItem := RPNCellRange3D(sheetIndex1, r1, c1, sheetIndex2, r2, c2, flags, ARPNItem);
|
||||
end;
|
||||
end; }
|
||||
|
||||
procedure TsSpreadBIFF5Reader.ReadRPNSheetIndex(AStream: TStream;
|
||||
out ASheet1, ASheet2: Integer);
|
||||
out ADocumentURL: String; out ASheet1, ASheet2: Integer);
|
||||
var
|
||||
idx: Int16;
|
||||
s: String;
|
||||
begin
|
||||
// One-based index to EXTERNSHEET record. Negative to indicate a 3D reference.
|
||||
// Positive to indicate an external reference
|
||||
ADocumentURL := '';
|
||||
ASheet1 := -1;
|
||||
ASheet2 := -1;
|
||||
|
||||
{ One-based index to EXTERNSHEET record. Negative to indicate a 3D reference.
|
||||
Positive to indicate an external reference }
|
||||
idx := WordLEToN(AStream.ReadWord);
|
||||
|
||||
// We don't support external references at the moment.
|
||||
if idx > 0 then begin
|
||||
ASheet1 := -1;
|
||||
ASheet1 := -1;
|
||||
exit;
|
||||
if idx < 0 then begin
|
||||
{ *** Internal 3d reference *** }
|
||||
|
||||
// Skip 8 unused bytes
|
||||
AStream.Position := AStream.Position + 8;
|
||||
|
||||
// Zero-based index to first referenced sheet (-1 = deleted sheet)
|
||||
idx := Int16(WordLEToN(AStream.ReadWord));
|
||||
if idx <> -1 then begin
|
||||
s := FExternSheets.Strings[idx];
|
||||
ASheet1 := FWorkbook.GetWorksheetIndex(s);
|
||||
end;
|
||||
|
||||
// Zero-based index to last referenced sheet (-1 = deleted sheet)
|
||||
idx := WordLEToN(AStream.ReadWord);
|
||||
if idx <> -1 then begin
|
||||
s := FExternSheets.Strings[idx];
|
||||
ASheet2 := FWorkbook.GetWorksheetIndex(s);
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
{ *** External reference *** }
|
||||
|
||||
// Skip 12 unused byes
|
||||
AStream.Position := AStream.Position + 12;
|
||||
|
||||
dec(idx); // 1-based index to 0-based index
|
||||
s := FExternSheets[idx];
|
||||
ADocumentURL := s;
|
||||
|
||||
// NOTE: THIS IS NOT COMPLETE !!!
|
||||
end;
|
||||
|
||||
// Skip 8 unused bytes
|
||||
AStream.Position := AStream.Position + 8;
|
||||
|
||||
// Zero-based index to first referenced sheet (-1 = deleted sheet)
|
||||
idx := WordLEToN(AStream.ReadWord);
|
||||
ASheet1 := idx;
|
||||
|
||||
// Zero-based index to last referenced sheet (-1 = deleted sheet)
|
||||
idx := WordLEToN(AStream.ReadWord);
|
||||
ASheet2 := idx;
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF5Reader.ReadRSTRING(AStream: TStream);
|
||||
@@ -1172,7 +1197,6 @@ var
|
||||
pane: Byte;
|
||||
begin
|
||||
{ Write workbook globals }
|
||||
|
||||
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
|
||||
|
||||
WriteCODEPAGE(AStream, FCodePage);
|
||||
@@ -1180,8 +1204,6 @@ begin
|
||||
WritePROTECT(AStream, bpLockStructure in Workbook.Protection);
|
||||
WritePASSWORD(AStream, Workbook.CryptoInfo);
|
||||
WriteGlobalLinkTable(AStream);
|
||||
// WriteEXTERNCOUNT(AStream);
|
||||
// WriteEXTERNSHEET(AStream);
|
||||
WriteDefinedNames(AStream);
|
||||
WriteWINDOW1(AStream);
|
||||
WriteFonts(AStream);
|
||||
@@ -1229,7 +1251,7 @@ begin
|
||||
WritePageSetup(AStream);
|
||||
|
||||
// Local link table
|
||||
WriteLocalLinkTable(AStream);
|
||||
WriteLocalLinkTable(AStream, FWorksheet);
|
||||
|
||||
// Protection
|
||||
if FWorksheet.IsProtected then begin
|
||||
@@ -1750,7 +1772,7 @@ begin
|
||||
|
||||
WriteEXTERNCOUNT(AStream, L.Count);
|
||||
for i:=0 to L.Count-1 do
|
||||
WriteEXTERNSHEET(AStream, L[i]);
|
||||
WriteEXTERNSHEET(AStream, L[i], true);
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
@@ -1883,38 +1905,46 @@ end;
|
||||
Writes a local Link Table with EXTERNCOUNT and EXTERSHEET records for
|
||||
internal 3D references to other sheets
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF5Writer.WriteLocalLinkTable(AStream: TStream);
|
||||
procedure TsSpreadBIFF5Writer.WriteLocalLinkTable(AStream: TStream;
|
||||
AWorksheet: TsWorksheet);
|
||||
var
|
||||
L: TStringList;
|
||||
cell: PCell;
|
||||
found: Boolean;
|
||||
i, n: Integer;
|
||||
i, j, n: Integer;
|
||||
sheetref: PsBIFFExternSheet;
|
||||
book: TsBIFFExternBook;
|
||||
sheet: TsWorksheet;
|
||||
begin
|
||||
L := TStringList.Create;
|
||||
try
|
||||
// Check whether there is any cell with a formula with 3D reference
|
||||
found := false;
|
||||
for cell in FWorksheet.Cells do
|
||||
if HasFormula(cell) and (pos('!', cell^.FormulaValue) <> 0) then begin
|
||||
found := true;
|
||||
break;
|
||||
end;
|
||||
// Write every sheet to local link table. This is too much - it would be
|
||||
// enough to write only those sheets involved in a 3d reference - but it
|
||||
// simplifies processing of 3d references a lot.
|
||||
if found then begin
|
||||
n := FWorkbook.GetWorksheetCount;
|
||||
if n > 0 then begin
|
||||
WriteEXTERNCOUNT(AStream, word(n));
|
||||
for i := 0 to n-1 do
|
||||
WriteEXTERNSHEET(AStream, FWorkbook.GetWorksheetByIndex(i).Name);
|
||||
CollectExternData(FWorksheet);
|
||||
if (FExternBooks = nil) or (FExternSheets = nil) then
|
||||
exit;
|
||||
|
||||
// Write the count of records in the local link table
|
||||
n := FExternSheets.Count;
|
||||
WriteEXTERNCOUNT(AStream, word(n));
|
||||
|
||||
// Write a EXTERNSHEET record for each linked sheet
|
||||
for i := 0 to n-1 do begin
|
||||
sheetref := FExternSheets[i];
|
||||
book := FExternBooks[sheetref^.ExternBookIndex];
|
||||
if book.Kind = ebkInternal then
|
||||
begin
|
||||
for j := sheetref^.FirstSheetIndex to sheetref^.LastSheetIndex do
|
||||
begin
|
||||
sheet := FWorkbook.GetWorksheetByIndex(j);
|
||||
if sheet = AWorksheet then
|
||||
WriteEXTERNSHEET(AStream, '', true)
|
||||
else
|
||||
WriteEXTERNSHEET(AStream, sheet.Name, true);
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
// Handle external links here
|
||||
end;
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
(*
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes a 3D cell address consisting of worksheet, row and column indexes.
|
||||
Needed for references to other worksheets within the same workbook.
|
||||
@@ -1947,6 +1977,57 @@ begin
|
||||
|
||||
Result := AStream.Position - p;
|
||||
end;
|
||||
*)
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes the sheet indexes of a 3D cell address consisting.
|
||||
Needed for references to other worksheets within the same workbook.
|
||||
Must be followed by WriteRPNCellAddress or WriteRPNCellAddressRange.
|
||||
Returns the number of bytes written or $FFFF if the feature is not supported.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsSpreadBIFF5Writer.WriteRPNSheetIndex(AStream: TStream;
|
||||
ADocumentURL: String; ASheet1, ASheet2: Integer): Word;
|
||||
var
|
||||
p: Int64;
|
||||
bookidx: Integer;
|
||||
book: TsBIFFExternBook;
|
||||
refidx: Integer;
|
||||
sheetref: PsBIFFExternSheet;
|
||||
begin
|
||||
if ADocumentURL <> '' then // Supporting only internal links
|
||||
exit;
|
||||
|
||||
p := AStream.Position;
|
||||
|
||||
// Find stored information on this link
|
||||
bookidx := FExternBooks.FindBook(ADocumentURL);
|
||||
refidx := FExternSheets.FindSheets(ADocumentURL, ASheet1, ASheet2);
|
||||
sheetref := FExternSheets[refidx];
|
||||
book := FExternBooks[sheetRef^.ExternBookIndex];
|
||||
if book.Kind = ebkExternal then
|
||||
exit($FFFF);
|
||||
|
||||
// One-based index of the EXTERNBOOK record to which this reference belongs.
|
||||
// For internal references ("3D references") this must be written as a
|
||||
// negative value.
|
||||
AStream.WriteWord(WordToLE(word(-(bookidx+1))));
|
||||
|
||||
// 8 unused bytes
|
||||
AStream.WriteDWord(0);
|
||||
AStream.WriteDWord(0);
|
||||
|
||||
// Zero-based index to first referenced sheet (FFFFH = deleted sheet)
|
||||
AStream.WriteWord(WordToLE(ASheet1));
|
||||
|
||||
// Single sheet reference
|
||||
if ASheet2 < 0 then ASheet2 := ASheet1;
|
||||
|
||||
// Zero-based index to last referenced sheet (FFFFH = deleted sheet)
|
||||
AStream.WriteWord(WordToLE(ASheet2));
|
||||
|
||||
Result := AStream.Position - p;
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 5 STRING record which immediately follows a FORMULA record
|
||||
|
@@ -64,21 +64,6 @@ uses
|
||||
fpsutils;
|
||||
|
||||
type
|
||||
TExternBookKind = (ebkExternal, ebkInternal, ebkAddInFunc, ebkDDE_OLE);
|
||||
|
||||
TBIFF8ExternBook = class
|
||||
Kind: TExternBookKind;
|
||||
DocumentURL: String;
|
||||
SheetNames: String;
|
||||
function GetSheetName(AIndex: Integer): String;
|
||||
end;
|
||||
|
||||
TBIFF8ExternSheet = packed record
|
||||
ExternBookIndex: Word;
|
||||
FirstSheetIndex: Word;
|
||||
LastSheetIndex: Word;
|
||||
end;
|
||||
|
||||
{ TsSpreadBIFF8Reader }
|
||||
TsSpreadBIFF8Reader = class(TsSpreadBIFFReader)
|
||||
private
|
||||
@@ -89,7 +74,7 @@ type
|
||||
FCommentID: Integer;
|
||||
FCommentLen: Integer;
|
||||
FBiff8ExternBooks: TFPObjectList;
|
||||
FBiff8ExternSheets: array of TBiff8ExternSheet;
|
||||
FBiff8ExternSheets: array of TsBiffExternSheet;
|
||||
function ReadString(const AStream: TStream; const ALength: Word;
|
||||
out ARichTextParams: TsRichTextParams): String;
|
||||
function ReadUnformattedWideString(const AStream: TStream;
|
||||
@@ -104,7 +89,7 @@ type
|
||||
procedure ReadCONTINUE(const AStream: TStream);
|
||||
procedure ReadDEFINEDNAME(const AStream: TStream);
|
||||
procedure ReadEXTERNBOOK(const AStream: TStream);
|
||||
procedure ReadEXTERNSHEET(const AStream: TStream);
|
||||
procedure ReadEXTERNSHEET(const AStream: TStream); virtual;
|
||||
procedure ReadFONT(const AStream: TStream);
|
||||
procedure ReadFORMAT(AStream: TStream); override;
|
||||
procedure ReadHeaderFooter(AStream: TStream; AIsHeader: Boolean); override;
|
||||
@@ -122,11 +107,12 @@ type
|
||||
out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); override;
|
||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override;
|
||||
function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; override;
|
||||
// function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; override;
|
||||
procedure ReadRPNCellRangeOffset(AStream: TStream;
|
||||
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
|
||||
out AFlags: TsRelFlags); override;
|
||||
procedure ReadRPNSheetIndex(AStream: TStream; out ASheet1, ASheet2: Integer); override;
|
||||
procedure ReadRPNSheetIndex(AStream: TStream; out ADocumentURL: String;
|
||||
out ASheet1, ASheet2: Integer); override;
|
||||
procedure ReadRSTRING(AStream: TStream);
|
||||
procedure ReadSST(const AStream: TStream);
|
||||
function ReadString_8bitLen(AStream: TStream): String; override;
|
||||
@@ -203,12 +189,16 @@ type
|
||||
out AContinueInString: Boolean): Boolean;
|
||||
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): word; override;
|
||||
(*
|
||||
function WriteRPNCellAddress3D(AStream: TStream; ASheet, ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): Word; override;
|
||||
*)
|
||||
function WriteRPNCellOffset(AStream: TStream; ARowOffset, AColOffset: Integer;
|
||||
AFlags: TsRelFlags): Word; override;
|
||||
function WriteRPNCellRangeAddress(AStream: TStream; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags): Word; override;
|
||||
function WriteRPNSheetIndex(AStream: TStream; ADocumentURL: String;
|
||||
ASheet1, ASheet2: Integer): Word; override;
|
||||
procedure WriteSST(AStream: TStream);
|
||||
function WriteString_8bitLen(AStream: TStream; AString: String): Integer; override;
|
||||
procedure WriteSTRINGRecord(AStream: TStream; AString: string); override;
|
||||
@@ -506,27 +496,6 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
{ TBiff8ExternBook }
|
||||
|
||||
function TBiff8ExternBook.GetSheetName(AIndex: Integer): String;
|
||||
var
|
||||
L: TStrings;
|
||||
begin
|
||||
L := TStringList.Create;
|
||||
try
|
||||
L.Delimiter := #1;
|
||||
L.StrictDelimiter := true;
|
||||
L.DelimitedText := SheetNames;
|
||||
if (AIndex >= 0) and (AIndex < L.Count) then
|
||||
Result := L[AIndex]
|
||||
else
|
||||
Result := '';
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TsSpreadBIFF8Reader }
|
||||
|
||||
constructor TsSpreadBIFF8Reader.Create(AWorkbook: TsWorkbook);
|
||||
@@ -1295,7 +1264,7 @@ begin
|
||||
if (c2 and MASK_EXCEL_RELATIVE_COL <> 0) then Include(AFlags, rfRelCol2);
|
||||
if (c2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
|
||||
end;
|
||||
|
||||
{
|
||||
function TsSpreadBIFF8Reader.ReadRPNCellRange3D(AStream: TStream;
|
||||
var ARPNItem: PRPNItem): Boolean;
|
||||
var
|
||||
@@ -1317,7 +1286,7 @@ begin
|
||||
sheetIndex2, r2, c2,
|
||||
flags, ARPNItem
|
||||
);
|
||||
end;
|
||||
end;}
|
||||
|
||||
{ Reads the difference between row and column corner indexes of a cell range
|
||||
and a reference cell.
|
||||
@@ -1351,26 +1320,28 @@ begin
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadRPNSheetIndex(AStream: TStream;
|
||||
out ASheet1, ASheet2: Integer);
|
||||
out ADocumentURL: String; out ASheet1, ASheet2: Integer);
|
||||
var
|
||||
refIndex: Word;
|
||||
ref: TBiff8ExternSheet;
|
||||
extbook: TBiff8ExternBook;
|
||||
ref: TsBiffExternSheet;
|
||||
book: TsBiffExternBook;
|
||||
begin
|
||||
// Index to REF entry in EXTERNSHEET record
|
||||
refIndex := WordLEToN(AStream.ReadWord);
|
||||
|
||||
ref := FBiff8ExternSheets[refIndex];
|
||||
extBook := FBiff8ExternBooks[ref.ExternBookIndex] as TBiff8ExternBook;
|
||||
book := FBiff8ExternBooks[ref.ExternBookIndex] as TsBiffExternBook;
|
||||
|
||||
// Only links to internal sheets supported so far.
|
||||
if extBook.Kind <> ebkInternal then
|
||||
if book.Kind <> ebkInternal then
|
||||
begin
|
||||
ADocumentURL := '';
|
||||
ASheet1 := -1;
|
||||
ASheet2 := -1;
|
||||
exit;
|
||||
end;
|
||||
|
||||
ADocumentURL := book.DocumentURL;
|
||||
ASheet1 := ref.FirstSheetIndex;
|
||||
ASheet2 := ref.LastSheetIndex;
|
||||
end;
|
||||
@@ -1870,14 +1841,14 @@ var
|
||||
i, n: Integer;
|
||||
url: widestring;
|
||||
sheetnames: widestring;
|
||||
externbook: TBiff8Externbook;
|
||||
book: TsBiffExternbook;
|
||||
p: Int64;
|
||||
t: array[0..1] of byte = (0, 0);
|
||||
begin
|
||||
if FBiff8ExternBooks = nil then
|
||||
FBiff8ExternBooks := TFPObjectList.Create(true);
|
||||
FBiff8ExternBooks := TsBIFFExternBookList.Create(true);
|
||||
|
||||
externBook := TBiff8ExternBook.Create;
|
||||
book := TsBiffExternBook.Create;
|
||||
|
||||
// Count of sheets in book
|
||||
n := WordLEToN(AStream.ReadWord);
|
||||
@@ -1886,22 +1857,23 @@ begin
|
||||
p := AStream.Position;
|
||||
AStream.ReadBuffer(t[0], 2);
|
||||
if (t[0] = 1) and (t[1] = 4) then
|
||||
externbook.Kind := ebkInternal
|
||||
book.Kind := ebkInternal
|
||||
else
|
||||
if (t[0] = 1) and (t[1] = $3A) then
|
||||
externbook.Kind := ebkAddInFunc
|
||||
book.Kind := ebkAddInFunc
|
||||
else if n = 0 then
|
||||
externbook.Kind := ebkDDE_OLE
|
||||
book.Kind := ebkDDE_OLE
|
||||
else
|
||||
externbook.Kind := ebkExternal;
|
||||
book.Kind := ebkExternal;
|
||||
|
||||
if (externbook.Kind = ebkExternal) then
|
||||
{ External workbook }
|
||||
if (book.Kind = ebkExternal) then
|
||||
begin
|
||||
AStream.Position := p;
|
||||
|
||||
// Encoded URL without sheet name (Unicode string, 16bit string length)
|
||||
url := ReadWideString(AStream, false);
|
||||
externbook.DocumentURL := UTF8Encode(url);
|
||||
book.DocumentURL := UTF8Encode(url);
|
||||
|
||||
if n = 0 then
|
||||
sheetnames := ''
|
||||
@@ -1911,10 +1883,10 @@ begin
|
||||
for i := 2 to n do
|
||||
sheetnames := sheetnames + widechar(#1) + ReadWideString(AStream, false);
|
||||
end;
|
||||
externbook.SheetNames := UTF8Encode(sheetNames);
|
||||
book.SheetNames := UTF8Encode(sheetNames);
|
||||
end;
|
||||
|
||||
FBiff8ExternBooks.Add(externbook);
|
||||
FBiff8ExternBooks.Add(book);
|
||||
end;
|
||||
|
||||
{ Reads an EXTERNSHEET record. Needed for 3d-references, named cells and
|
||||
@@ -2352,6 +2324,8 @@ var
|
||||
i: Integer;
|
||||
pane: Byte;
|
||||
begin
|
||||
CollectExternData;
|
||||
|
||||
{ Write workbook globals }
|
||||
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
|
||||
WriteCodePage(AStream, 'ucs2le'); // = utf-16
|
||||
@@ -2874,7 +2848,41 @@ end;
|
||||
procedure TsSpreadBIFF8Writer.WriteEXTERNSHEET(AStream: TStream);
|
||||
var
|
||||
n, i: Integer;
|
||||
sheetRef: PsBIFFExternSheet;
|
||||
book: TsBIFFExternBook;
|
||||
begin
|
||||
if (FExternSheets = nil) or (FExternBooks = nil) then
|
||||
exit;
|
||||
|
||||
{ Count the following REF structures }
|
||||
{ We support only internal links. Once external links are supported the
|
||||
following code probably can be dropped. }
|
||||
n := 0;
|
||||
for i := 0 to FExternSheets.Count-1 do begin
|
||||
sheetRef := FExternSheets[i];
|
||||
book := FExternBooks[sheetRef^.ExternBookIndex];
|
||||
if (book.Kind = ebkInternal) then inc(n);
|
||||
end;
|
||||
|
||||
{ BIFF record header }
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_EXTERNSHEET, 2 + 6*n);
|
||||
|
||||
{ Write the determined count of REF structures }
|
||||
AStream.WriteWord(WordToLE(n));
|
||||
|
||||
for i:= 0 to FExternSheets.Count-1 do begin
|
||||
sheetRef := FExternSheets[i];
|
||||
book := FExternBooks[sheetRef^.ExternBookIndex];
|
||||
if (book.Kind = ebkInternal) then
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(sheetRef^.ExternBookIndex));
|
||||
AStream.WriteWord(WordToLE(sheetRef^.FirstSheetIndex));
|
||||
AStream.WriteWord(WordToLE(sheetRef^.LastSheetIndex));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
(*
|
||||
{ Since sheet range are not supported we simply note every sheet here. }
|
||||
n := FWorkbook.GetWorksheetCount;
|
||||
|
||||
@@ -2892,6 +2900,7 @@ begin
|
||||
AStream.WriteWord(WordToLE(i)); // Index to last sheet in EXTERNBOOK sheet list
|
||||
end;
|
||||
end;
|
||||
*)
|
||||
(*
|
||||
|
||||
write a record for
|
||||
@@ -3741,7 +3750,7 @@ begin
|
||||
AStream.WriteWord(WordToLE(c));
|
||||
Result := 4;
|
||||
end;
|
||||
|
||||
(*
|
||||
function TsSpreadBIFF8Writer.WriteRPNCellAddress3D(AStream: TStream;
|
||||
ASheet, ARow, ACol: Cardinal; AFlags: TsRelFlags): Word;
|
||||
begin
|
||||
@@ -3753,7 +3762,7 @@ begin
|
||||
// Write row/column address
|
||||
Result := 2 + WriteRPNCellAddress(AStream, ARow, ACol, AFlags);
|
||||
end;
|
||||
|
||||
*)
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes row and column offset needed in RPN formulas (unsigned integers!)
|
||||
@@ -3803,6 +3812,20 @@ begin
|
||||
Result := 8;
|
||||
end;
|
||||
|
||||
function TsSpreadBIFF8Writer.WriteRPNSheetIndex(AStream: TStream;
|
||||
ADocumentURL: String; ASheet1, ASheet2: Integer): Word;
|
||||
var
|
||||
idx: Integer;
|
||||
begin
|
||||
idx := FExternSheets.FindSheets(ADocumentURL, ASheet1, ASheet2);
|
||||
if idx = -1 then
|
||||
Result := $FFFE
|
||||
else begin
|
||||
AStream.WriteWord(WordToLE(word(idx)));
|
||||
Result := 2;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Writes a buffer containing a string (with header) and its associated
|
||||
rich-text parameters to an EXCEL record.
|
||||
Since the size in an EXCEL8 record is limited to 8224 bytes
|
||||
|
@@ -10,7 +10,7 @@ OpenOffice Microsoft Excel File Format document }
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, DateUtils, lconvencoding,
|
||||
Classes, SysUtils, DateUtils, lconvencoding, contnrs,
|
||||
fpsTypes, fpSpreadsheet, fpsUtils, fpsNumFormat, fpsPalette,
|
||||
fpsReaderWriter, fpsrpn;
|
||||
|
||||
@@ -379,6 +379,53 @@ type
|
||||
property ValidOnSheet: Integer read FValidOnSheet;
|
||||
end;
|
||||
|
||||
{ TsExternBook - Information on where out-of-sheet references are stored. }
|
||||
TsBIFFExternBookKind = (ebkExternal, ebkInternal, ebkAddInFunc, ebkDDE_OLE);
|
||||
TsBIFFExternBook = class
|
||||
Kind: TsBIFFExternBookKind;
|
||||
// The following fields are used only for external workbooks.
|
||||
DocumentURL: String;
|
||||
SheetNames: String; // List of worksheetnames separated by #1
|
||||
function GetWorksheetName(AIndex: Integer): String;
|
||||
end;
|
||||
|
||||
{ TsBIFFExternBookList }
|
||||
TsBIFFExternBookList = class(TFPObjectlist)
|
||||
private
|
||||
function GetItem(AIndex: Integer): TsBIFFExternBook;
|
||||
procedure SetItem(AIndex: Integer; AValue: TsBIFFExternBook);
|
||||
public
|
||||
function AddBook(ABookName: String): Integer;
|
||||
function AddInternal: Integer;
|
||||
function FindBook(ABookName: String): Integer;
|
||||
function FindInternalBook: Integer;
|
||||
property Item[AIndex: Integer]: TsBIFFExternBook read GetItem write SetItem; default;
|
||||
end;
|
||||
|
||||
{ TsExternSheet - Information on a sheets used in out-of-sheet references }
|
||||
TsBIFFExternSheet = packed record
|
||||
ExternBookIndex: Word;
|
||||
FirstSheetIndex: Word;
|
||||
LastSheetIndex: Word;
|
||||
end;
|
||||
PsBIFFExternSheet = ^TsBIFFExternSheet;
|
||||
|
||||
{ A list for sheets used in out-of-sheet references }
|
||||
TsBIFFExternSheetList = class(TFPList)
|
||||
private
|
||||
FBookList: TsBIFFExternBookList;
|
||||
function GetItem(AIndex: Integer): PsBIFFExternSheet;
|
||||
procedure SetItem(AIndex: Integer; AValue: PsBIFFExternSheet);
|
||||
public
|
||||
constructor Create(ABookList: TsBIFFExternBookList);
|
||||
destructor Destroy; override;
|
||||
function AddSheets(ABookName: String; ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||
procedure Clear;
|
||||
function FindSheets(ABookName: String; ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||
property Item[AIndex: Integer]: PsBIFFExternSheet read GetItem write SetItem; default;
|
||||
end;
|
||||
|
||||
|
||||
{ TsSpreadBIFFReader }
|
||||
TsSpreadBIFFReader = class(TsCustomSpreadReader)
|
||||
protected
|
||||
@@ -435,8 +482,10 @@ type
|
||||
procedure ReadDefColWidth(AStream: TStream);
|
||||
// Read the default row height
|
||||
procedure ReadDefRowHeight(AStream: TStream);
|
||||
// Read an EXTERNCOUNT record
|
||||
procedure ReadEXTERNCOUNT(AStream: TStream);
|
||||
// Read an EXTERNSHEET record (defined names)
|
||||
procedure ReadExternSheet(AStream: TStream);
|
||||
procedure ReadEXTERNSHEET(AStream: TStream); virtual;
|
||||
// Read FORMAT record (cell formatting)
|
||||
procedure ReadFormat(AStream: TStream); virtual;
|
||||
// Read FORMULA record
|
||||
@@ -478,13 +527,14 @@ type
|
||||
out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); virtual;
|
||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); virtual;
|
||||
function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; virtual;
|
||||
// function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; virtual;
|
||||
procedure ReadRPNCellRangeOffset(AStream: TStream;
|
||||
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
|
||||
out AFlags: TsRelFlags); virtual;
|
||||
function ReadRPNFunc(AStream: TStream): Word; virtual;
|
||||
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); virtual;
|
||||
procedure ReadRPNSheetIndex(AStream: TStream; out ASheet1, ASheet2: Integer); virtual;
|
||||
procedure ReadRPNSheetIndex(AStream: TStream; out ADocumentURL: String;
|
||||
out ASheet1, ASheet2: Integer); virtual;
|
||||
function ReadRPNTokenArray(AStream: TStream; ACell: PCell;
|
||||
ASharedFormulaBase: PCell = nil): Boolean; overload;
|
||||
function ReadRPNTokenArray(AStream: TStream; ARpnTokenArraySize: Word;
|
||||
@@ -525,8 +575,11 @@ type
|
||||
FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding
|
||||
FFirstNumFormatIndexInFile: Integer;
|
||||
FPalette: TsPalette;
|
||||
FExternBooks: TsBIFFExternBookList;
|
||||
FExternSheets: TsBIFFExternSheetList;
|
||||
|
||||
procedure AddBuiltinNumFormats; override;
|
||||
procedure CollectExternData(AWorksheet: TsWorksheet = nil);
|
||||
function FindXFIndex(AFormatIndex: Integer): Integer; virtual;
|
||||
function FixLineEnding(const AText: String): String;
|
||||
function FormulaSupported(ARPNFormula: TsRPNFormula; out AUnsupported: String): Boolean;
|
||||
@@ -574,7 +627,8 @@ type
|
||||
// Writes out an EXTERNCOUNT record
|
||||
procedure WriteEXTERNCOUNT(AStream: TStream; ACount: Word);
|
||||
// Writes out an EXTERNSHEET record
|
||||
procedure WriteEXTERNSHEET(AStream: TStream; ASheetName: String);
|
||||
procedure WriteEXTERNSHEET(AStream: TStream; ASheetName: String;
|
||||
IsInternalSheet: Boolean);
|
||||
// Writes out a FORMAT record
|
||||
procedure WriteFORMAT(AStream: TStream; ANumFormatStr: String;
|
||||
ANumFormatIndex: Integer); virtual;
|
||||
@@ -611,8 +665,10 @@ type
|
||||
|
||||
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): Word; virtual;
|
||||
(*
|
||||
function WriteRPNCellAddress3D(AStream: TStream; ASheet, ARow, ACol: Cardinal;
|
||||
{%H-}AFlags: TsRelFlags): Word; virtual;
|
||||
*)
|
||||
function WriteRPNCellOffset(AStream: TStream; ARowOffset, AColOffset: Integer;
|
||||
AFlags: TsRelFlags): Word; virtual;
|
||||
function WriteRPNCellRangeAddress(AStream: TStream; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
@@ -621,6 +677,8 @@ type
|
||||
AFormula: TsRPNFormula; ACell: PCell); virtual;
|
||||
function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; virtual;
|
||||
procedure WriteRPNResult(AStream: TStream; ACell: PCell);
|
||||
function WriteRPNSheetIndex(AStream: TStream; ADocumentURL: String;
|
||||
ASheet1, ASheet2: Integer): word; virtual;
|
||||
procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell;
|
||||
AFormula: TsRPNFormula; UseRelAddr, IsSupported: Boolean; var RPNLength: Word);
|
||||
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
|
||||
@@ -900,6 +958,196 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
{ -----------------------------------------------------------------------------}
|
||||
{ TsBIFFExternBook }
|
||||
{ -----------------------------------------------------------------------------}
|
||||
function TsBIFFExternBook.GetWorksheetName(AIndex: Integer): String;
|
||||
var
|
||||
L: TStrings;
|
||||
begin
|
||||
Result := '';
|
||||
if Kind = ebkExternal then begin
|
||||
L := TStringList.Create;
|
||||
try
|
||||
L.Delimiter := #1;
|
||||
L.DelimitedText := SheetNames;
|
||||
Result := L[AIndex];
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{------------------------------------------------------------------------------}
|
||||
{ TsBIFFExternBookList }
|
||||
{------------------------------------------------------------------------------}
|
||||
function TsBIFFExternBookList.AddBook(ABookName: String): Integer;
|
||||
var
|
||||
book: TsBIFFExternBook;
|
||||
begin
|
||||
if ABookName = '' then
|
||||
Result := AddInternal
|
||||
else
|
||||
begin
|
||||
Result := FindBook(ABookName);
|
||||
if Result = -1 then begin
|
||||
book := TsBIFFExternBook.Create;
|
||||
book.DocumentURL := ABookName;
|
||||
book.Kind := ebkExternal;
|
||||
Result := Add(book);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TsBIFFExternBookList.AddInternal: Integer;
|
||||
var
|
||||
book: TsBIFFExternBook;
|
||||
begin
|
||||
Result := FindInternalBook;
|
||||
if Result = -1 then begin
|
||||
book := TsBIFFExternBook.Create;
|
||||
book.Kind := ebkInternal;
|
||||
Result := Add(book);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TsBIFFExternBookList.FindBook(ABookName: String): Integer;
|
||||
var
|
||||
book: TsBIFFExternBook;
|
||||
begin
|
||||
if ABookName = '' then
|
||||
Result := FindInternalBook
|
||||
else
|
||||
begin
|
||||
for Result:=0 to Count-1 do
|
||||
begin
|
||||
book := Item[Result];
|
||||
if (book.Kind = ebkExternal) and (book.DocumentURL = ABookName) then
|
||||
exit;
|
||||
end;
|
||||
Result := -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TsBIFFExternBookList.FindInternalBook: Integer;
|
||||
begin
|
||||
for Result := 0 to Count-1 do
|
||||
if Item[Result].Kind = ebkInternal then exit;
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
function TsBIFFExternBookList.GetItem(AIndex: Integer): TsBIFFExternBook;
|
||||
begin
|
||||
Result := TsBIFFExternBook(inherited Items[AIndex]);
|
||||
end;
|
||||
|
||||
procedure TsBIFFExternBookList.SetItem(AIndex: Integer;
|
||||
AValue: TsBIFFExternBook);
|
||||
begin
|
||||
inherited Items[AIndex] := AValue;
|
||||
end;
|
||||
|
||||
|
||||
{------------------------------------------------------------------------------}
|
||||
{ TsBiffExternSheetList }
|
||||
{------------------------------------------------------------------------------}
|
||||
constructor TsBIFFExternSheetList.Create(ABookList: TsBIFFExternBookList);
|
||||
begin
|
||||
inherited Create;
|
||||
FBookList := ABookList;
|
||||
end;
|
||||
|
||||
destructor TsBIFFExternSheetList.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TsBIFFExternSheetList.AddSheets(ABookName: String;
|
||||
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||
var
|
||||
P: PsBIFFExternSheet;
|
||||
idx: Integer;
|
||||
begin
|
||||
Result := FindSheets(ABookName, ASheetIndex1, ASheetIndex2);
|
||||
if Result = -1 then
|
||||
begin
|
||||
New(P);
|
||||
idx := FBookList.FindBook(ABookName);
|
||||
if idx = -1 then
|
||||
idx := FBookList.AddBook(ABookName);
|
||||
P^.ExternBookIndex := idx;
|
||||
|
||||
if ASheetIndex2 = -1 then
|
||||
ASheetIndex2 := ASheetIndex1;
|
||||
if ASheetIndex2 < ASheetIndex1 then
|
||||
begin
|
||||
P^.FirstSheetIndex := ASheetIndex2;
|
||||
P^.LastSheetIndex := ASheetIndex1;
|
||||
end else
|
||||
begin
|
||||
P^.FirstSheetIndex := ASheetIndex1;
|
||||
P^.LastSheetIndex := ASheetIndex2;
|
||||
end;
|
||||
Result := Add(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TsBIFFExternSheetList.Clear;
|
||||
var
|
||||
i: Integer;
|
||||
P: PsBIFFExternSheet;
|
||||
begin
|
||||
for i:=0 to Count-1 do begin
|
||||
P := Item[i];
|
||||
Dispose(P);
|
||||
end;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TsBIFFExternSheetList.FindSheets(ABookName: String;
|
||||
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||
var
|
||||
book: TsBIFFExternBook;
|
||||
P: PsBIFFExternSheet;
|
||||
tmp: Integer;
|
||||
idx: Integer;
|
||||
begin
|
||||
if ASheetIndex2 = -1 then ASheetIndex2 := ASheetIndex1;
|
||||
if ASheetIndex2 < ASheetIndex1 then begin
|
||||
tmp := ASheetIndex1;
|
||||
ASheetIndex1 := ASheetIndex2;
|
||||
ASheetIndex2 := tmp;
|
||||
end;
|
||||
|
||||
idx := FBookList.FindBook(ABookName);
|
||||
if idx = -1 then
|
||||
exit(-1);
|
||||
|
||||
for Result:=0 to Count-1 do begin
|
||||
P := Item[Result];
|
||||
if (P^.ExternBookIndex = idx) and
|
||||
(P^.FirstSheetIndex = ASheetIndex1) and
|
||||
(P^.LastSheetIndex = ASheetIndex2)
|
||||
then
|
||||
exit;
|
||||
end;
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
function TsBIFFExternSheetList.GetItem(AIndex: Integer): PsBIFFExternSheet;
|
||||
begin
|
||||
Result := PsBIFFExternSheet(inherited Items[AIndex]);
|
||||
end;
|
||||
|
||||
procedure TsBIFFExternSheetList.SetItem(AIndex: Integer; AValue: PsBIFFExternSheet);
|
||||
begin
|
||||
inherited Items[AIndex] := AValue;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{------------------------------------------------------------------------------}
|
||||
{ TsBIFFDefinedName }
|
||||
{------------------------------------------------------------------------------}
|
||||
@@ -983,7 +1231,6 @@ begin
|
||||
FCellFormatList := TsCellFormatList.Create(true);
|
||||
// true = allow duplicates! XF indexes get out of sync if not all format records are in list
|
||||
|
||||
FExternSheets := TStringList.Create;
|
||||
FDefinedNames := TFPList.Create;
|
||||
|
||||
// Initial base date in case it won't be read from file
|
||||
@@ -1610,6 +1857,14 @@ begin
|
||||
FWorksheet.WriteDefaultRowHeight(TwipsToPts(hw), suPoints);
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFFReader.ReadEXTERNCOUNT(AStream: TStream);
|
||||
begin
|
||||
AStream.ReadWord;
|
||||
{ We ignore the value of the record, but use the presence of the record
|
||||
to create the FExternSheets list. }
|
||||
FExternSheets := TStringList.Create;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
In the file format versions up to BIFF5 (incl) this record stores the name of
|
||||
an external document and a sheet name inside of this document.
|
||||
@@ -1634,7 +1889,7 @@ begin
|
||||
AStream.ReadBuffer(ansistr[2], len-1);
|
||||
ansistr[1] := char(b);
|
||||
s := ConvertEncoding(ansistr, FCodePage, encodingUTF8);
|
||||
FExternSheets.Add(s);
|
||||
FExternSheets.AddObject(s, TObject(PtrInt(b)));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@@ -2399,13 +2654,14 @@ begin
|
||||
if (r2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
|
||||
end;
|
||||
|
||||
{
|
||||
function TsSpreadBIFFReader.ReadRPNCellRange3D(AStream: TStream;
|
||||
var ARPNItem: PRPNItem): Boolean;
|
||||
begin
|
||||
Unused(AStream, ARPNItem);
|
||||
Result := false; // "false" means: "not supported"
|
||||
// must be overridden
|
||||
end;
|
||||
end; }
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Reads the difference between row and column corner indexes of a cell range
|
||||
@@ -2475,9 +2731,10 @@ end;
|
||||
in a formula from the current stream position.
|
||||
Place holder - must be overridden! }
|
||||
procedure TsSpreadBIFFReader.ReadRPNSheetIndex(AStream: TStream;
|
||||
out ASheet1, ASheet2: Integer);
|
||||
out ADocumentURL: String; out ASheet1, ASheet2: Integer);
|
||||
begin
|
||||
Unused(AStream);
|
||||
ADocumentURL := '';
|
||||
ASheet1 := -1;
|
||||
ASheet2 := -1;
|
||||
end;
|
||||
@@ -2678,6 +2935,7 @@ var
|
||||
b: Byte;
|
||||
found: Boolean;
|
||||
sheet1, sheet2: Integer;
|
||||
url: String;
|
||||
begin
|
||||
rpnItem := nil;
|
||||
p0 := AStream.Position;
|
||||
@@ -2730,28 +2988,53 @@ begin
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TREF3D_V:
|
||||
begin
|
||||
ReadRPNSheetIndex(AStream, sheet1, sheet2);
|
||||
ReadRPNCellAddress(AStream, r, c, flags);
|
||||
rpnItem := RpnCellValue3D(sheet1, r, c, flags, rpnItem);
|
||||
ReadRPNSheetIndex(AStream, url, sheet1, sheet2);
|
||||
if url <> '' then begin
|
||||
supported := false;
|
||||
FWorkbook.AddErrorMsg('External links are not supported.');
|
||||
end else begin
|
||||
ReadRPNCellAddress(AStream, r, c, flags);
|
||||
rpnItem := RpnCellValue3D(sheet1, r, c, flags, rpnItem);
|
||||
end;
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TREF3D_R: //, INT_EXCEL_TOKEN_TREF3d_V:
|
||||
begin
|
||||
ReadRPNSheetIndex(AStream, sheet1, sheet2);
|
||||
ReadRPNCellAddressOffset(AStream, dr, dc, flags);
|
||||
if (rfRelRow in flags)
|
||||
then r := LongInt(ACell^.Row) + dr
|
||||
else r := dr;
|
||||
if (rfRelCol in flags)
|
||||
then c := LongInt(ACell^.Col) + dc
|
||||
else c := dc;
|
||||
case token of
|
||||
INT_EXCEL_TOKEN_TREF3D_V: rpnItem := RpnCellValue3D(sheet1, r, c, flags, rpnItem);
|
||||
INT_EXCEL_TOKEN_TREF3D_R: rpnItem := RpnCellRef3D(sheet1, r, c, flags, rpnItem);
|
||||
ReadRPNSheetIndex(AStream, url, sheet1, sheet2);
|
||||
if url <> '' then begin
|
||||
supported := false;
|
||||
FWorkbook.AddErrorMsg('External links are not supported.');
|
||||
end else
|
||||
begin
|
||||
ReadRPNCellAddressOffset(AStream, dr, dc, flags); // Is this usage of rel addresses correct?
|
||||
if (rfRelRow in flags)
|
||||
then r := LongInt(ACell^.Row) + dr
|
||||
else r := dr;
|
||||
if (rfRelCol in flags)
|
||||
then c := LongInt(ACell^.Col) + dc
|
||||
else c := dc;
|
||||
case token of
|
||||
INT_EXCEL_TOKEN_TREF3D_V: rpnItem := RpnCellValue3D(sheet1, r, c, flags, rpnItem);
|
||||
INT_EXCEL_TOKEN_TREF3D_R: rpnItem := RpnCellRef3D(sheet1, r, c, flags, rpnItem);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TAREA3D_R:
|
||||
begin
|
||||
if not ReadRPNCellRange3D(AStream, rpnItem) then supported := false;
|
||||
ReadRPNSheetIndex(AStream, url, sheet1, sheet2);
|
||||
if url <> '' then begin
|
||||
supported := false;
|
||||
FWorkbook.AddErrorMsg('External links are not supported.');
|
||||
end else
|
||||
if (sheet1 = -1) and (sheet2 = -1) then
|
||||
supported := false
|
||||
else
|
||||
begin
|
||||
ReadRPNCellRangeAddress(AStream, r, c, r2, c2, flags);
|
||||
if r2 = $FFFF then r2 := Cardinal(-1);
|
||||
if c2 = $FF then c2 := Cardinal(-1);
|
||||
rpnItem := RPNCellRange3D(sheet1, r, c, sheet2, r2, c2, flags, rpnItem);
|
||||
end;
|
||||
// was: if not ReadRPNCellRange3D(AStream, rpnItem) then supported := false;
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TREFN_A:
|
||||
begin
|
||||
@@ -3155,6 +3438,8 @@ end;
|
||||
|
||||
destructor TsSpreadBIFFWriter.Destroy;
|
||||
begin
|
||||
FExternSheets.Free;
|
||||
FExternBooks.Free;
|
||||
FPalette.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
@@ -3186,6 +3471,70 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Collects the data for out-of-sheet links found in the specified worksheet
|
||||
(or all worksheets if the parameter is omitted).
|
||||
The found data are written to the FExternBooks and FExternSheets lists.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsWorksheet = nil);
|
||||
|
||||
procedure DoCollectForSheet(ASheet: TsWorksheet);
|
||||
var
|
||||
cell: PCell;
|
||||
parser: TsExpressionParser;
|
||||
rpn: TsRPNFormula;
|
||||
fe: TsFormulaElement;
|
||||
j: Integer;
|
||||
begin
|
||||
for cell in ASheet.Cells do
|
||||
begin
|
||||
if not HasFormula(cell) then
|
||||
Continue;
|
||||
if not (cf3dFormula in cell^.Flags) then
|
||||
Continue;
|
||||
|
||||
parser := TsSpreadsheetParser.Create(ASheet);
|
||||
try
|
||||
parser.Expression := cell^.FormulaValue;
|
||||
rpn := parser.RPNFormula;
|
||||
for j:=0 to High(rpn) do
|
||||
begin
|
||||
fe := rpn[j];
|
||||
if fe.ElementKind in [fekCell3d, fekCellRef3d, fekCellRange3d] then
|
||||
FExternSheets.AddSheets('', fe.Sheet, fe.Sheet2); // '' --> supporting only internal 3d links so far
|
||||
end;
|
||||
finally
|
||||
parser.Free;
|
||||
rpn := nil;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
sheet: TsWorksheet;
|
||||
i: Integer;
|
||||
begin
|
||||
FExternBooks.Free;
|
||||
FExternBooks := TsBIFFExternBookList.Create;
|
||||
FExternSheets.Free;
|
||||
FExternSheets := TsBIFFExternSheetList.Create(FExternBooks);
|
||||
|
||||
if AWorksheet <> nil then
|
||||
DoCollectForSheet(AWorksheet)
|
||||
else
|
||||
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
||||
begin
|
||||
sheet := FWorkbook.GetWorksheetbyIndex(i);
|
||||
DoCollectForSheet(sheet);
|
||||
end;
|
||||
|
||||
if FExternSheets.Count = 0 then begin
|
||||
FreeAndNil(FExternSheets);
|
||||
FreeAndNil(FExternBooks);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Determines the index of the XF record, according to formatting of the
|
||||
given format index; this format index is taken from a cell, row, or column
|
||||
@@ -3752,24 +4101,38 @@ end;
|
||||
Valid for BIFF2-BIFF5.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFFWriter.WriteEXTERNSHEET(AStream: TStream;
|
||||
ASheetName: String);
|
||||
ASheetName: String; IsInternalSheet: Boolean);
|
||||
var
|
||||
s: ansistring;
|
||||
n: Byte;
|
||||
begin
|
||||
// Convert to ANSI
|
||||
s := ConvertEncoding(ASheetName, encodingUTF8, FCodePage);
|
||||
n := Length(s);
|
||||
|
||||
{ BIFF record header }
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_EXTERNSHEET, 2 + Length(s));
|
||||
|
||||
{ Character count in worksheet name - don't count the following flag! }
|
||||
AStream.WriteByte(Length(s));
|
||||
|
||||
{ Flag for identification as own sheet }
|
||||
AStream.WriteByte($03);
|
||||
|
||||
{ Sheet name }
|
||||
AStream.WriteBuffer(s[1], Length(s));
|
||||
if IsInternalSheet then
|
||||
begin
|
||||
if ASheetName = '' then
|
||||
begin
|
||||
// The current worksheet is linked: no sheet name to be written!
|
||||
AStream.WriteByte(1); // Length info
|
||||
AStream.WriteByte(2); // code for current worksheet
|
||||
end else
|
||||
begin
|
||||
// Another internal worksheet is links - code 3 followed by sheet name
|
||||
// Note: Code is not cosidered in the string length!
|
||||
AStream.WriteByte(n); // Length byte
|
||||
AStream.WriteByte(3); // code byte
|
||||
AStream.WriteBuffer(s[1], n); // Sheet name characters
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
// External sheets
|
||||
// Not suppored at the moment - they have code 2
|
||||
end;
|
||||
end;
|
||||
(*
|
||||
procedure TsSpreadBIFFWriter.WriteEXTERNSHEET(AStream: TStream);
|
||||
@@ -4360,7 +4723,7 @@ begin
|
||||
// Number of bytes written
|
||||
Result := 3;
|
||||
end;
|
||||
|
||||
(*
|
||||
{@ -----------------------------------------------------------------------------
|
||||
Writes the address of a cell as used in an RPN formula and returns the
|
||||
count of bytes written. The return value is $FFFF if 3D addresses are not
|
||||
@@ -4373,7 +4736,7 @@ begin
|
||||
Unused(AStream, ASheet);
|
||||
Unused(ARow, ACol);
|
||||
Result := 0;
|
||||
end;
|
||||
end;*)
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes row and column offset (unsigned integers!)
|
||||
@@ -4575,6 +4938,20 @@ begin
|
||||
end;
|
||||
*)
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writew the first and last worksheet indexes of a 3D cell range.
|
||||
Must be followed by WriteRPNCellAddress or WriteRPNCellAddressRange.
|
||||
Returns the number of bytes written, or $FFFF if 3D references are not
|
||||
supported.
|
||||
Valid for BIFF2 only (which does not support 3D refences). Must be overridden
|
||||
for BIFF5 and BIFF8.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsSpreadBIFFWriter.WriteRPNSheetIndex(AStream: TStream;
|
||||
ADocumentURL: String; ASheet1, ASheet2: Integer): Word;
|
||||
begin
|
||||
Result := $FFFF; // --> not supported by default.
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes the token array of the given RPN formula and returns its size
|
||||
-------------------------------------------------------------------------------}
|
||||
@@ -4656,16 +5033,13 @@ begin
|
||||
|
||||
INT_EXCEL_TOKEN_TREF3D_V: { fekCell3D }
|
||||
begin
|
||||
n := WriteRPNCellAddress3D(
|
||||
AStream,
|
||||
AFormula[i].Sheet,
|
||||
AFormula[i].Row, AFormula[i].Col,
|
||||
AFormula[i].RelFlags
|
||||
);
|
||||
n := WriteRPNSheetIndex(AStream, '', AFormula[i].Sheet, AFormula[i].Sheet2);
|
||||
if n = $FFFF then
|
||||
FWorkbook.AddErrorMsg('3D cell addresses are not supported.')
|
||||
else
|
||||
else begin
|
||||
inc(n, WriteRPNCellAddress(AStream, AFormula[i].Row, AFormula[i].Col, AFormula[i].RelFlags));
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TAREA_R: { fekCellRange }
|
||||
@@ -4679,6 +5053,23 @@ begin
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TAREA3D_R: { fekCellRange3D }
|
||||
begin
|
||||
n := WriteRPNSheetIndex(AStream, '', AFormula[i].Sheet, AFormula[i].Sheet2);
|
||||
if n = $FFFF then
|
||||
FWorkbook.AddErrorMsg('3D cell address ranges are not supported.')
|
||||
else if n = $FFF3 then
|
||||
FWorkbook.AddErrorMsg('Sheets not found in LinkTable.')
|
||||
else begin
|
||||
inc(n, WriteRPNCellRangeAddress(AStream,
|
||||
AFormula[i].Row, AFormula[i].Col,
|
||||
AFormula[i].Row2, AFormula[i].Col2,
|
||||
AFormula[i].RelFlags)
|
||||
);
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TREFN_R,
|
||||
INT_EXCEL_TOKEN_TREFN_V,
|
||||
INT_EXCEL_TOKEN_TREFN_A: { fekCellOffset }
|
||||
|
Reference in New Issue
Block a user