You've already forked lazarus-ccr
fpspreadsheet: Read print ranges and repeated print rows and columns from BIFF5 and BIFF8 files (xls - BIFF2 does not support them).
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4512 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -51,6 +51,10 @@ function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
|
|||||||
ANext: PRPNItem): PRPNItem; overload;
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
function RPNCellOffset(ARowOffset, AColOffset: Integer; AFlags: TsRelFlags;
|
function RPNCellOffset(ARowOffset, AColOffset: Integer; AFlags: TsRelFlags;
|
||||||
ANext: PRPNItem): PRPNItem;
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
function RPNCellRef3D(ASheet, ARow, ACol: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
function RPNCellRange3D(ASheet1, ARow1, ACol1, ASheet2, ARow2, ACol2: Integer;
|
||||||
|
AFlags: TsRelFlags; ANext: PRPNItem): PRPNItem;
|
||||||
function RPNErr(AErrCode: TsErrorValue; ANext: PRPNItem): PRPNItem;
|
function RPNErr(AErrCode: TsErrorValue; ANext: PRPNItem): PRPNItem;
|
||||||
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
|
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
|
||||||
function RPNMissingArg(ANext: PRPNItem): PRPNItem;
|
function RPNMissingArg(ANext: PRPNItem): PRPNItem;
|
||||||
@ -82,6 +86,7 @@ begin
|
|||||||
New(Result);
|
New(Result);
|
||||||
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
||||||
Result^.FE.StringValue := '';
|
Result^.FE.StringValue := '';
|
||||||
|
Result^.FE.SheetNames := '';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -254,6 +259,30 @@ begin
|
|||||||
Result^.Next := ANext;
|
Result^.Next := ANext;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function RPNCellRef3D(ASheet, ARow, ACol: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekCellRef3d;
|
||||||
|
Result^.FE.Sheet := ASheet;
|
||||||
|
Result^.FE.Row := ARow;
|
||||||
|
Result^.FE.Col := ACol;
|
||||||
|
Result^.FE.RelFlags := AFlags;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function RPNCellRange3D(ASheet1, ARow1, ACol1, ASheet2, ARow2, ACol2: Integer;
|
||||||
|
AFlags: TsRelFlags; ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := RPNCellRef3d(ASheet1, ARow1, ACol1, AFlags, ANext);
|
||||||
|
Result^.FE.ElementKind := fekCellRange3D;
|
||||||
|
Result^.FE.Sheet2 := ASheet2;
|
||||||
|
Result^.FE.Row2 := ARow2;
|
||||||
|
Result^.FE.Col2 := ACol2;
|
||||||
|
Result^.FE.RelFlags := AFlags;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Creates an entry in the RPN array with an error value.
|
Creates an entry in the RPN array with an error value.
|
||||||
|
|
||||||
@ -437,6 +466,7 @@ begin
|
|||||||
nextitem := item^.Next;
|
nextitem := item^.Next;
|
||||||
Result[n] := item^.FE;
|
Result[n] := item^.FE;
|
||||||
Result[n].StringValue := item^.FE.StringValue;
|
Result[n].StringValue := item^.FE.StringValue;
|
||||||
|
Result[n].Sheetnames := item^.FE.SheetNames;
|
||||||
if AReverse then dec(n) else inc(n);
|
if AReverse then dec(n) else inc(n);
|
||||||
DisposeRPNItem(item);
|
DisposeRPNItem(item);
|
||||||
item := nextitem;
|
item := nextitem;
|
||||||
|
@ -104,13 +104,15 @@ type
|
|||||||
}
|
}
|
||||||
TFEKind = (
|
TFEKind = (
|
||||||
{ Basic operands }
|
{ Basic operands }
|
||||||
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
|
fekCell, fekCellRef, fekCellRange, fekCellOffset,
|
||||||
fekString, fekBool, fekErr, fekMissingArg,
|
fekCellRef3d, fekCellRange3d,
|
||||||
|
fekNum, fekInteger, fekString, fekBool, fekErr, fekMissingArg,
|
||||||
{ Basic operations }
|
{ Basic operations }
|
||||||
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
|
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
|
||||||
fekConcat, // string concatenation
|
fekConcat, // string concatenation
|
||||||
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
|
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
|
||||||
fekParen, // show parenthesis around expression node
|
fekList, // List operator
|
||||||
|
fekParen, // show parenthesis around expression node -- don't add anything after fekParen!
|
||||||
{ Functions - they are identified by their name }
|
{ Functions - they are identified by their name }
|
||||||
fekFunc
|
fekFunc
|
||||||
);
|
);
|
||||||
@ -139,16 +141,17 @@ const
|
|||||||
|
|
||||||
type
|
type
|
||||||
{@@ Elements of an expanded formula.
|
{@@ Elements of an expanded formula.
|
||||||
Note: If ElementKind is fekCellOffset, "Row" and "Col" have to be cast
|
Note: If ElementKind is fekCellOffset, "Row" and "Col" have to be cast to signed integers! }
|
||||||
to signed integers! }
|
|
||||||
TsFormulaElement = record
|
TsFormulaElement = record
|
||||||
ElementKind: TFEKind;
|
ElementKind: TFEKind;
|
||||||
Row, Row2: Cardinal; // zero-based
|
Row, Row2: Cardinal; // zero-based
|
||||||
Col, Col2: Cardinal; // zero-based
|
Col, Col2: Cardinal; // zero-based
|
||||||
|
Sheet, Sheet2: Integer; // zero-based
|
||||||
|
SheetNames: String; // both sheet names separated by a TAB character (intermediate use only)
|
||||||
DoubleValue: double;
|
DoubleValue: double;
|
||||||
IntValue: Word;
|
IntValue: Word;
|
||||||
StringValue: String;
|
StringValue: String;
|
||||||
RelFlags: TsRelFlags; // store info on relative/absolute addresses
|
RelFlags: TsRelFlags; // info on relative/absolute addresses
|
||||||
FuncName: String;
|
FuncName: String;
|
||||||
ParamsNum: Byte;
|
ParamsNum: Byte;
|
||||||
end;
|
end;
|
||||||
@ -569,6 +572,15 @@ type
|
|||||||
{@@ Array with cell ranges }
|
{@@ Array with cell ranges }
|
||||||
TsCellRangeArray = array of TsCellRange;
|
TsCellRangeArray = array of TsCellRange;
|
||||||
|
|
||||||
|
{@@ Record combining sheet index and row/column corner indexes of a cell range }
|
||||||
|
TsCellRange3d = record
|
||||||
|
Row1, Col1, Row2, Col2: Cardinal;
|
||||||
|
Sheet1, Sheet2: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ Array of 3d cell ranges }
|
||||||
|
TsCellRange3dArray = array of TsCellRange3d;
|
||||||
|
|
||||||
{@@ Record containing limiting indexes of column or row range }
|
{@@ Record containing limiting indexes of column or row range }
|
||||||
TsRowColRange = record
|
TsRowColRange = record
|
||||||
FirstIndex, LastIndex: Cardinal;
|
FirstIndex, LastIndex: Cardinal;
|
||||||
|
@ -115,6 +115,25 @@ type
|
|||||||
procedure TestWriteRead_BIFF5_HeaderFooterFontColor_2sheets;
|
procedure TestWriteRead_BIFF5_HeaderFooterFontColor_2sheets;
|
||||||
procedure TestWriteRead_BIFF5_HeaderFooterFontColor_3sheets;
|
procedure TestWriteRead_BIFF5_HeaderFooterFontColor_3sheets;
|
||||||
|
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_1sheet_1Range_NoSpace;
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_1sheet_2Ranges_NoSpace;
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_2sheet_1Range_NoSpace;
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_2sheet_2Ranges_NoSpace;
|
||||||
|
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_1sheet_1Range_Space;
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_1sheet_2Ranges_Space;
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_2sheet_1Range_Space;
|
||||||
|
procedure TestWriteRead_BIFF5_PrintRanges_2sheet_2Ranges_Space;
|
||||||
|
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedRow_0;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedRows_0_1;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedRows_1_3;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedCol_0;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedCols_0_1;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedCols_1_3;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedCol_0_Row_0;
|
||||||
|
procedure TestWriteRead_BIFF5_RepeatedCols_0_1_Rows_0_1;
|
||||||
|
|
||||||
{ BIFF8 page layout tests }
|
{ BIFF8 page layout tests }
|
||||||
procedure TestWriteRead_BIFF8_PageMargins_1sheet_0;
|
procedure TestWriteRead_BIFF8_PageMargins_1sheet_0;
|
||||||
procedure TestWriteRead_BIFF8_PageMargins_1sheet_1;
|
procedure TestWriteRead_BIFF8_PageMargins_1sheet_1;
|
||||||
@ -169,6 +188,25 @@ type
|
|||||||
procedure TestWriteRead_BIFF8_HeaderFooterFontColor_2sheets;
|
procedure TestWriteRead_BIFF8_HeaderFooterFontColor_2sheets;
|
||||||
procedure TestWriteRead_BIFF8_HeaderFooterFontColor_3sheets;
|
procedure TestWriteRead_BIFF8_HeaderFooterFontColor_3sheets;
|
||||||
|
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_1sheet_1Range_NoSpace;
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_1sheet_2Ranges_NoSpace;
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_2sheet_1Range_NoSpace;
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_2sheet_2Ranges_NoSpace;
|
||||||
|
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_1sheet_1Range_Space;
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_1sheet_2Ranges_Space;
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_2sheet_1Range_Space;
|
||||||
|
procedure TestWriteRead_BIFF8_PrintRanges_2sheet_2Ranges_Space;
|
||||||
|
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedRow_0;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedRows_0_1;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedRows_1_3;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedCol_0;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedCols_0_1;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedCols_1_3;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedCol_0_Row_0;
|
||||||
|
procedure TestWriteRead_BIFF8_RepeatedCols_0_1_Rows_0_1;
|
||||||
|
|
||||||
{ OOXML page layout tests }
|
{ OOXML page layout tests }
|
||||||
procedure TestWriteRead_OOXML_PageMargins_1sheet_0;
|
procedure TestWriteRead_OOXML_PageMargins_1sheet_0;
|
||||||
procedure TestWriteRead_OOXML_PageMargins_1sheet_1;
|
procedure TestWriteRead_OOXML_PageMargins_1sheet_1;
|
||||||
@ -1138,6 +1176,87 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_1sheet_1Range_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 1, 1, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_1sheet_2Ranges_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 1, 2, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_2sheet_1Range_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 2, 1, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_2sheet_2Ranges_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 2, 2, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_1sheet_1Range_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 1, 1, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_1sheet_2Ranges_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 1, 2, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_2sheet_1Range_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 2, 1, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PrintRanges_2sheet_2Ranges_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel5, 2, 2, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedRow_0;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, -1, -1, 0, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedRows_0_1;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, -1, -1, 0, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedRows_1_3;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, -1, -1, 1, 3);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedCol_0;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, 0, 0, -1, -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedCols_0_1;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, 0, 1, -1, -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedCols_1_3;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, 1, 3, -1, -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedCol_0_Row_0;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, 0, 0, 0, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_RepeatedCols_0_1_Rows_0_1;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel5, 0, 1, 0, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Tests for BIFF8 file format }
|
{ Tests for BIFF8 file format }
|
||||||
|
|
||||||
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PageMargins_1sheet_0;
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PageMargins_1sheet_0;
|
||||||
@ -1363,6 +1482,87 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_1sheet_1Range_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 1, 1, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_1sheet_2Ranges_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 1, 2, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_2sheet_1Range_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 2, 1, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_2sheet_2Ranges_NoSpace;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 2, 2, false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_1sheet_1Range_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 1, 1, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_1sheet_2Ranges_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 1, 2, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_2sheet_1Range_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 2, 1, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PrintRanges_2sheet_2Ranges_Space;
|
||||||
|
begin
|
||||||
|
TestWriteRead_PrintRanges(sfExcel8, 2, 2, true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedRow_0;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, -1, -1, 0, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedRows_0_1;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, -1, -1, 0, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedRows_1_3;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, -1, -1, 1, 3);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedCol_0;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, 0, 0, -1, -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedCols_0_1;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, 0, 1, -1, -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedCols_1_3;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, 1, 3, -1, -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedCol_0_Row_0;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, 0, 0, 0, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_RepeatedCols_0_1_Rows_0_1;
|
||||||
|
begin
|
||||||
|
TestWriteRead_RepeatedColRows(sfExcel8, 0, 1, 0, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Tests for OOXML file format }
|
{ Tests for OOXML file format }
|
||||||
|
|
||||||
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_PageMargins_1sheet_0;
|
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_PageMargins_1sheet_0;
|
||||||
|
@ -59,7 +59,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, fpcanvas, lconvencoding,
|
Classes, SysUtils, fpcanvas, lconvencoding,
|
||||||
fpsTypes, fpspreadsheet,
|
fpsTypes, fpspreadsheet, fpsrpn,
|
||||||
xlscommon,
|
xlscommon,
|
||||||
{$ifdef USE_NEW_OLE}
|
{$ifdef USE_NEW_OLE}
|
||||||
fpolebasic,
|
fpolebasic,
|
||||||
@ -69,7 +69,6 @@ uses
|
|||||||
fpsUtils;
|
fpsUtils;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
{ TsSpreadBIFF5Reader }
|
{ TsSpreadBIFF5Reader }
|
||||||
|
|
||||||
TsSpreadBIFF5Reader = class(TsSpreadBIFFReader)
|
TsSpreadBIFF5Reader = class(TsSpreadBIFFReader)
|
||||||
@ -77,9 +76,11 @@ type
|
|||||||
procedure PopulatePalette; override;
|
procedure PopulatePalette; override;
|
||||||
{ Record writing methods }
|
{ Record writing methods }
|
||||||
procedure ReadBoundsheet(AStream: TStream);
|
procedure ReadBoundsheet(AStream: TStream);
|
||||||
|
procedure ReadDEFINEDNAME(AStream: TStream);
|
||||||
procedure ReadFONT(const AStream: TStream);
|
procedure ReadFONT(const AStream: TStream);
|
||||||
procedure ReadFORMAT(AStream: TStream); override;
|
procedure ReadFORMAT(AStream: TStream); override;
|
||||||
procedure ReadLABEL(AStream: TStream); override;
|
procedure ReadLABEL(AStream: TStream); override;
|
||||||
|
function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; override;
|
||||||
procedure ReadRSTRING(AStream: TStream);
|
procedure ReadRSTRING(AStream: TStream);
|
||||||
procedure ReadStandardWidth(AStream: TStream; ASheet: TsWorksheet);
|
procedure ReadStandardWidth(AStream: TStream; ASheet: TsWorksheet);
|
||||||
procedure ReadStringRecord(AStream: TStream); override;
|
procedure ReadStringRecord(AStream: TStream); override;
|
||||||
@ -88,7 +89,6 @@ type
|
|||||||
procedure ReadXF(AStream: TStream);
|
procedure ReadXF(AStream: TStream);
|
||||||
public
|
public
|
||||||
{ General reading methods }
|
{ General reading methods }
|
||||||
// procedure ReadFromFile(AFileName: string); override;
|
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -346,7 +346,9 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TsSpreadBIFF5Reader }
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsSpreadBIFF5Reader }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Populates the reader's default palette using the BIFF5 default colors.
|
Populates the reader's default palette using the BIFF5 default colors.
|
||||||
@ -357,6 +359,96 @@ begin
|
|||||||
FPalette.UseColors(PALETTE_BIFF5);
|
FPalette.UseColors(PALETTE_BIFF5);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Reads a BOUNDSHEET record containing a worksheet name
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFF5Reader.ReadBoundsheet(AStream: TStream);
|
||||||
|
var
|
||||||
|
Len: Byte;
|
||||||
|
s: AnsiString;
|
||||||
|
sheetName: String;
|
||||||
|
begin
|
||||||
|
{ Absolute stream position of the BOF record of the sheet represented
|
||||||
|
by this record }
|
||||||
|
// Just assume that they are in order
|
||||||
|
AStream.ReadDWord();
|
||||||
|
|
||||||
|
{ Visibility }
|
||||||
|
AStream.ReadByte();
|
||||||
|
|
||||||
|
{ Sheet type }
|
||||||
|
AStream.ReadByte();
|
||||||
|
|
||||||
|
{ Sheet name: Byte string, 8-bit length }
|
||||||
|
Len := AStream.ReadByte();
|
||||||
|
|
||||||
|
SetLength(s, Len);
|
||||||
|
AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar));
|
||||||
|
sheetName := ConvertEncoding(s, FCodePage, EncodingUTF8);
|
||||||
|
FWorksheetNames.Add(sheetName);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Reads a DEFINEDNAME record. Currently only extracts print ranges and titles.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFF5Reader.ReadDEFINEDNAME(AStream: TStream);
|
||||||
|
var
|
||||||
|
options: Word;
|
||||||
|
len: byte;
|
||||||
|
formulaSize: Word;
|
||||||
|
ansistr: ansiString;
|
||||||
|
defName: String;
|
||||||
|
rpnformula: TsRPNFormula;
|
||||||
|
extsheetIndex: Integer;
|
||||||
|
sheetIndex: Integer;
|
||||||
|
begin
|
||||||
|
// Options
|
||||||
|
options := WordLEToN(AStream.ReadWord);
|
||||||
|
if options and $0020 = 0 then // only support built-in names at the moment!
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// Keyboard shortcut --> ignore
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of name (character count)
|
||||||
|
len := AStream.ReadByte;
|
||||||
|
|
||||||
|
// Size of formula data
|
||||||
|
formulasize := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
|
// EXTERNSHEET index (1-base), or 0 if global name
|
||||||
|
extsheetIndex := SmallInt(WordLEToN(AStream.ReadWord)) - 1; // now 0-based!
|
||||||
|
|
||||||
|
// Sheet index (1-based) on which the name is valid (0 = global)
|
||||||
|
sheetIndex := SmallInt(WordLEToN(AStream.ReadWord)) - 1; // now 0-based!
|
||||||
|
|
||||||
|
// Length of Menu text (ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of description text(ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of help topic text (ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of status bar text (ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Name
|
||||||
|
SetLength(ansistr, len);
|
||||||
|
AStream.ReadBuffer(ansistr[1], len);
|
||||||
|
defName := ConvertEncoding(ansistr, FCodepage, encodingUTF8);
|
||||||
|
|
||||||
|
// Formula
|
||||||
|
if not ReadRPNTokenArray(AStream, formulaSize, rpnFormula) then
|
||||||
|
exit;
|
||||||
|
// Store defined name in internal list
|
||||||
|
FDefinedNames.Add(TsBIFFDefinedName.Create(defName, rpnFormula, sheetIndex));
|
||||||
|
|
||||||
|
// Skip rest...
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TsSpreadBIFF5Reader.ReadWorkbookGlobals(AStream: TStream);
|
procedure TsSpreadBIFF5Reader.ReadWorkbookGlobals(AStream: TStream);
|
||||||
var
|
var
|
||||||
SectionEOF: Boolean = False;
|
SectionEOF: Boolean = False;
|
||||||
@ -372,14 +464,16 @@ begin
|
|||||||
CurStreamPos := AStream.Position;
|
CurStreamPos := AStream.Position;
|
||||||
|
|
||||||
case RecordType of
|
case RecordType of
|
||||||
INT_EXCEL_ID_BOF : ;
|
INT_EXCEL_ID_BOF : ;
|
||||||
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
|
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
|
||||||
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
|
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
|
||||||
INT_EXCEL_ID_FONT : ReadFont(AStream);
|
INT_EXCEL_ID_DEFINEDNAME : ReadDefinedName(AStream);
|
||||||
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
|
INT_EXCEL_ID_EXTERNSHEET : ReadExternSheet(AStream);
|
||||||
INT_EXCEL_ID_XF : ReadXF(AStream);
|
INT_EXCEL_ID_FONT : ReadFont(AStream);
|
||||||
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
|
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
|
||||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
INT_EXCEL_ID_XF : ReadXF(AStream);
|
||||||
|
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
|
||||||
|
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||||
else
|
else
|
||||||
// nothing
|
// nothing
|
||||||
end;
|
end;
|
||||||
@ -401,7 +495,7 @@ var
|
|||||||
RecordType: Word;
|
RecordType: Word;
|
||||||
CurStreamPos: Int64;
|
CurStreamPos: Int64;
|
||||||
begin
|
begin
|
||||||
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurrentWorksheet], true);
|
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurSheetIndex], true);
|
||||||
|
|
||||||
while (not SectionEOF) do
|
while (not SectionEOF) do
|
||||||
begin
|
begin
|
||||||
@ -497,30 +591,36 @@ begin
|
|||||||
FixRows(FWorksheet);
|
FixRows(FWorksheet);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFF5Reader.ReadBoundsheet(AStream: TStream);
|
function TsSpreadBIFF5Reader.ReadRPNCellRange3D(AStream: TStream;
|
||||||
|
var ARPNItem: PRPNItem): Boolean;
|
||||||
var
|
var
|
||||||
Len: Byte;
|
sheetIndex: SmallInt;
|
||||||
s: AnsiString;
|
sheetIndex1, sheetIndex2: Word;
|
||||||
sheetName: String;
|
r1, c1, r2, c2: Cardinal;
|
||||||
|
flags: TsRelFlags;
|
||||||
begin
|
begin
|
||||||
{ Absolute stream position of the BOF record of the sheet represented
|
Result := true;
|
||||||
by this record }
|
|
||||||
// Just assume that they are in order
|
|
||||||
AStream.ReadDWord();
|
|
||||||
|
|
||||||
{ Visibility }
|
// 1-based index to EXTERNSHEET record containing name of first referenced worksheet
|
||||||
AStream.ReadByte();
|
// negative for 3D reference, positive for external reference
|
||||||
|
sheetIndex := WordLEToN(AStream.ReadWord);
|
||||||
|
if sheetIndex > 0 then
|
||||||
|
exit(false); // we support only internal references here!
|
||||||
|
sheetIndex := abs(sheetindex) - 1; // make it a usable 0-based index although we don't need it any more...
|
||||||
|
|
||||||
{ Sheet type }
|
// Next 8 bytes not used.
|
||||||
AStream.ReadByte();
|
AStream.ReadDWord;
|
||||||
|
AStream.ReadDWord;
|
||||||
|
|
||||||
{ Sheet name: Byte string, 8-bit length }
|
// Zero-based index to first and last referenced sheet (in case of internal ref)
|
||||||
Len := AStream.ReadByte();
|
sheetIndex1 := WordLEToN(AStream.ReadWord);
|
||||||
|
sheetIndex2 := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
SetLength(s, Len);
|
// Cell range coordinates
|
||||||
AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar));
|
ReadRPNCellRangeAddress(AStream, r1, c1, r2, c2, flags);
|
||||||
sheetName := ConvertEncoding(s, FCodePage, EncodingUTF8);
|
if r2 = $FFFF then r2 := Cardinal(-1);
|
||||||
FWorksheetNames.Add(sheetName);
|
if c2 = $FF then c2 := Cardinal(-1);
|
||||||
|
ARPNItem := RPNCellRange3D(sheetIndex1, r1, c1, sheetIndex2, r2, c2, flags, ARPNItem);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFF5Reader.ReadRSTRING(AStream: TStream);
|
procedure TsSpreadBIFF5Reader.ReadRSTRING(AStream: TStream);
|
||||||
|
@ -56,7 +56,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, fpcanvas, DateUtils, contnrs, lazutf8,
|
Classes, SysUtils, fpcanvas, DateUtils, contnrs, lazutf8,
|
||||||
fpstypes, fpspreadsheet, xlscommon,
|
fpstypes, fpspreadsheet, fpsrpn, xlscommon,
|
||||||
{$ifdef USE_NEW_OLE}
|
{$ifdef USE_NEW_OLE}
|
||||||
fpolebasic,
|
fpolebasic,
|
||||||
{$else}
|
{$else}
|
||||||
@ -66,6 +66,12 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
TBIFF8ExternSheet = packed record
|
||||||
|
ExternBookIndex: Word;
|
||||||
|
FirstSheetIndex: Word;
|
||||||
|
LastSheetIndex: Word;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TsSpreadBIFF8Reader }
|
{ TsSpreadBIFF8Reader }
|
||||||
TsSpreadBIFF8Reader = class(TsSpreadBIFFReader)
|
TsSpreadBIFF8Reader = class(TsSpreadBIFFReader)
|
||||||
private
|
private
|
||||||
@ -75,22 +81,26 @@ type
|
|||||||
FCommentPending: Boolean;
|
FCommentPending: Boolean;
|
||||||
FCommentID: Integer;
|
FCommentID: Integer;
|
||||||
FCommentLen: Integer;
|
FCommentLen: Integer;
|
||||||
procedure ReadBoundsheet(AStream: TStream);
|
FBiff8ExternSheets: array of TBiff8ExternSheet;
|
||||||
function ReadString(const AStream: TStream; const ALength: Word;
|
function ReadString(const AStream: TStream; const ALength: Word;
|
||||||
out ARichTextParams: TsRichTextParams): String;
|
out ARichTextParams: TsRichTextParams): String;
|
||||||
function ReadUnformattedWideString(const AStream: TStream;
|
function ReadUnformattedWideString(const AStream: TStream;
|
||||||
const ALength: Word): WideString;
|
const ALength: Word): WideString;
|
||||||
function ReadWideString(const AStream: TStream; const ALength: Word;
|
function ReadWideString(const AStream: TStream; const ALength: Word;
|
||||||
out ARichTextParams: TsRichTextParams): WideString; overload;
|
out ARichTextParams: TsRichTextParams): WideString; overload;
|
||||||
function ReadWideString(const AStream: TStream; const AUse8BitLength: Boolean): WideString; overload;
|
function ReadWideString(const AStream: TStream;
|
||||||
|
const AUse8BitLength: Boolean): WideString; overload;
|
||||||
protected
|
protected
|
||||||
procedure PopulatePalette; override;
|
procedure PopulatePalette; override;
|
||||||
|
procedure ReadBOUNDSHEET(AStream: TStream);
|
||||||
procedure ReadCONTINUE(const AStream: TStream);
|
procedure ReadCONTINUE(const AStream: TStream);
|
||||||
|
procedure ReadDEFINEDNAME(const AStream: TStream);
|
||||||
|
procedure ReadEXTERNSHEET(const AStream: TStream);
|
||||||
procedure ReadFONT(const AStream: TStream);
|
procedure ReadFONT(const AStream: TStream);
|
||||||
procedure ReadFORMAT(AStream: TStream); override;
|
procedure ReadFORMAT(AStream: TStream); override;
|
||||||
procedure ReadHeaderFooter(AStream: TStream; AIsHeader: Boolean); override;
|
procedure ReadHeaderFooter(AStream: TStream; AIsHeader: Boolean); override;
|
||||||
procedure ReadHyperLink(AStream: TStream);
|
procedure ReadHyperLink(const AStream: TStream);
|
||||||
procedure ReadHyperlinkToolTip(AStream: TStream);
|
procedure ReadHyperlinkToolTip(const AStream: TStream);
|
||||||
procedure ReadLABEL(AStream: TStream); override;
|
procedure ReadLABEL(AStream: TStream); override;
|
||||||
procedure ReadLabelSST(const AStream: TStream);
|
procedure ReadLabelSST(const AStream: TStream);
|
||||||
procedure ReadMergedCells(const AStream: TStream);
|
procedure ReadMergedCells(const AStream: TStream);
|
||||||
@ -103,6 +113,7 @@ type
|
|||||||
out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); override;
|
out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); override;
|
||||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override;
|
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override;
|
||||||
|
function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; override;
|
||||||
procedure ReadRPNCellRangeOffset(AStream: TStream;
|
procedure ReadRPNCellRangeOffset(AStream: TStream;
|
||||||
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
|
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
|
||||||
out AFlags: TsRelFlags); override;
|
out AFlags: TsRelFlags); override;
|
||||||
@ -443,13 +454,14 @@ type
|
|||||||
Text: String;
|
Text: String;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TsSpreadBIFF8Reader }
|
{ TsSpreadBIFF8Reader }
|
||||||
|
|
||||||
destructor TsSpreadBIFF8Reader.Destroy;
|
destructor TsSpreadBIFF8Reader.Destroy;
|
||||||
var
|
var
|
||||||
j: Integer;
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
|
SetLength(FBiff8ExternSheets, 0);
|
||||||
|
|
||||||
if Assigned(FSharedStringTable) then
|
if Assigned(FSharedStringTable) then
|
||||||
begin
|
begin
|
||||||
for j := FSharedStringTable.Count-1 downto 0 do
|
for j := FSharedStringTable.Count-1 downto 0 do
|
||||||
@ -777,16 +789,18 @@ begin
|
|||||||
|
|
||||||
if RecordType <> INT_EXCEL_ID_CONTINUE then begin
|
if RecordType <> INT_EXCEL_ID_CONTINUE then begin
|
||||||
case RecordType of
|
case RecordType of
|
||||||
INT_EXCEL_ID_BOF : ;
|
INT_EXCEL_ID_BOF : ;
|
||||||
INT_EXCEL_ID_BOUNDSHEET: ReadBoundSheet(AStream);
|
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
|
||||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
INT_EXCEL_ID_DEFINEDNAME : ReadDEFINEDNAME(AStream);
|
||||||
INT_EXCEL_ID_SST : ReadSST(AStream);
|
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||||
INT_EXCEL_ID_CODEPAGE : ReadCodepage(AStream);
|
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream);
|
||||||
INT_EXCEL_ID_FONT : ReadFont(AStream);
|
INT_EXCEL_ID_SST : ReadSST(AStream);
|
||||||
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
|
INT_EXCEL_ID_CODEPAGE : ReadCodepage(AStream);
|
||||||
INT_EXCEL_ID_XF : ReadXF(AStream);
|
INT_EXCEL_ID_FONT : ReadFont(AStream);
|
||||||
INT_EXCEL_ID_DATEMODE : ReadDateMode(AStream);
|
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
|
||||||
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
|
INT_EXCEL_ID_XF : ReadXF(AStream);
|
||||||
|
INT_EXCEL_ID_DATEMODE : ReadDateMode(AStream);
|
||||||
|
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
|
||||||
else
|
else
|
||||||
// nothing
|
// nothing
|
||||||
end;
|
end;
|
||||||
@ -809,7 +823,7 @@ var
|
|||||||
RecordType: Word;
|
RecordType: Word;
|
||||||
CurStreamPos: Int64;
|
CurStreamPos: Int64;
|
||||||
begin
|
begin
|
||||||
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurrentWorksheet], true);
|
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurSheetIndex], true);
|
||||||
|
|
||||||
while (not SectionEOF) do
|
while (not SectionEOF) do
|
||||||
begin
|
begin
|
||||||
@ -829,6 +843,7 @@ begin
|
|||||||
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
|
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
|
||||||
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
|
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
|
||||||
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
|
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
|
||||||
|
INT_EXCEL_ID_DEFINEDNAME : ReadDefinedName(AStream);
|
||||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||||
INT_EXCEL_ID_FOOTER : ReadHeaderFooter(AStream, false);
|
INT_EXCEL_ID_FOOTER : ReadHeaderFooter(AStream, false);
|
||||||
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
||||||
@ -944,7 +959,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
(*
|
(*
|
||||||
const AStrea
|
|
||||||
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream);
|
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream);
|
||||||
var
|
var
|
||||||
BIFF8EOF: Boolean;
|
BIFF8EOF: Boolean;
|
||||||
@ -1179,6 +1193,26 @@ begin
|
|||||||
if (c2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
|
if (c2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TsSpreadBIFF8Reader.ReadRPNCellRange3D(AStream: TStream;
|
||||||
|
var ARPNItem: PRPNItem): Boolean;
|
||||||
|
var
|
||||||
|
sheetIndex: Integer;
|
||||||
|
r1, c1, r2, c2: Cardinal;
|
||||||
|
flags: TsRelFlags;
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
sheetIndex := WordLEToN(AStream.ReadWord);
|
||||||
|
if FBiff8ExternSheets[sheetIndex].ExternBookIndex <> 0 then
|
||||||
|
exit(false);
|
||||||
|
ReadRPNCellRangeAddress(AStream, r1, c1, r2, c2, flags);
|
||||||
|
if r2 = $FFFF then r2 := Cardinal(-1);
|
||||||
|
if c2 = $FF then c2 := Cardinal(-1);
|
||||||
|
ARPNItem := RPNCellRange3D(
|
||||||
|
FBiff8ExternSheets[sheetIndex].FirstSheetIndex, r1, c1,
|
||||||
|
FBiff8ExternSheets[sheetIndex].LastSheetIndex, r2, c2,
|
||||||
|
flags, ARPNItem);
|
||||||
|
end;
|
||||||
|
|
||||||
{ Reads the difference between row and column corner indexes of a cell range
|
{ Reads the difference between row and column corner indexes of a cell range
|
||||||
and a reference cell.
|
and a reference cell.
|
||||||
Overriding the implementation in xlscommon. }
|
Overriding the implementation in xlscommon. }
|
||||||
@ -1628,6 +1662,84 @@ begin
|
|||||||
FCellFormatList.Add(fmt);
|
FCellFormatList.Add(fmt);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Reads a DEFINEDNAME record. Currently only extract print ranges and titles. }
|
||||||
|
procedure TsSpreadBIFF8Reader.ReadDEFINEDNAME(const AStream: TStream);
|
||||||
|
var
|
||||||
|
options: Word;
|
||||||
|
len: byte;
|
||||||
|
formulaSize: Word;
|
||||||
|
widestr: WideString;
|
||||||
|
defName: String;
|
||||||
|
rpnformula: TsRPNFormula;
|
||||||
|
rtf: TsRichTextParams;
|
||||||
|
validOnSheet: Integer;
|
||||||
|
begin
|
||||||
|
// Options
|
||||||
|
options := WordLEToN(AStream.ReadWord);
|
||||||
|
if options and $0020 = 0 then // only support built-in names at the moment!
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// Keyboard shortcut --> ignore
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of name (character count)
|
||||||
|
len := AStream.ReadByte;
|
||||||
|
|
||||||
|
// Size of formula data
|
||||||
|
formulasize := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
|
// not used
|
||||||
|
AStream.ReadWord;
|
||||||
|
|
||||||
|
// Sheet index (1-based) on which the name is valid (0 = global)
|
||||||
|
validOnSheet := SmallInt(WordLEToN(AStream.ReadWord)) - 1; // now 0-based!
|
||||||
|
|
||||||
|
// Length of Menu text (ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of description text(ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of help topic text (ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Length of status bar text (ignore)
|
||||||
|
AStream.ReadByte;
|
||||||
|
|
||||||
|
// Name
|
||||||
|
wideStr := ReadWideString(AStream, len, rtf);
|
||||||
|
defName := UTF8Encode(widestr);
|
||||||
|
|
||||||
|
// Formula
|
||||||
|
if not ReadRPNTokenArray(AStream, formulaSize, rpnFormula) then
|
||||||
|
exit;
|
||||||
|
// Store defined name in internal list
|
||||||
|
FDefinedNames.Add(TsBIFFDefinedName.Create(defName, rpnFormula, validOnSheet));
|
||||||
|
|
||||||
|
// Skip rest...
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Reads an EXTERNSHEET record. Needed for named cells and print ranges. }
|
||||||
|
procedure TsSpreadBIFF8Reader.ReadEXTERNSHEET(const AStream: TStream);
|
||||||
|
var
|
||||||
|
numItems: Word;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
numItems := WordLEToN(AStream.ReadWord);
|
||||||
|
SetLength(FBiff8ExternSheets, numItems);
|
||||||
|
|
||||||
|
for i := 0 to numItems-1 do begin
|
||||||
|
AStream.ReadBuffer(FBiff8ExternSheets[i], Sizeof(FBiff8ExternSheets[i]));
|
||||||
|
with FBiff8ExternSheets[i] do
|
||||||
|
begin
|
||||||
|
ExternBookIndex := WordLEToN(ExternBookIndex);
|
||||||
|
FirstSheetIndex := WordLEToN(FirstSheetIndex);
|
||||||
|
LastSheetIndex := WordLEToN(LastSheetIndex);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Reads a FONT record. The retrieved font is stored in the workbook's FontList. }
|
{ Reads a FONT record. The retrieved font is stored in the workbook's FontList. }
|
||||||
procedure TsSpreadBIFF8Reader.ReadFONT(const AStream: TStream);
|
procedure TsSpreadBIFF8Reader.ReadFONT(const AStream: TStream);
|
||||||
var
|
var
|
||||||
@ -1761,7 +1873,7 @@ end;
|
|||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Reads a HYPERLINK record
|
Reads a HYPERLINK record
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF8Reader.ReadHyperlink(AStream: TStream);
|
procedure TsSpreadBIFF8Reader.ReadHyperlink(const AStream: TStream);
|
||||||
var
|
var
|
||||||
row, col, row1, col1, row2, col2: word;
|
row, col, row1, col1, row2, col2: word;
|
||||||
guid: TGUID;
|
guid: TGUID;
|
||||||
@ -1902,7 +2014,7 @@ end;
|
|||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Reads a HYPERLINK TOOLTIP record
|
Reads a HYPERLINK TOOLTIP record
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF8Reader.ReadHyperlinkToolTip(AStream: TStream);
|
procedure TsSpreadBIFF8Reader.ReadHyperlinkToolTip(const AStream: TStream);
|
||||||
var
|
var
|
||||||
txt: String;
|
txt: String;
|
||||||
widestr: widestring;
|
widestr: widestring;
|
||||||
|
@ -12,7 +12,7 @@ interface
|
|||||||
uses
|
uses
|
||||||
Classes, SysUtils, DateUtils, lconvencoding,
|
Classes, SysUtils, DateUtils, lconvencoding,
|
||||||
fpsTypes, fpSpreadsheet, fpsUtils, fpsNumFormatParser, fpsPalette,
|
fpsTypes, fpSpreadsheet, fpsUtils, fpsNumFormatParser, fpsPalette,
|
||||||
fpsReaderWriter;
|
fpsReaderWriter, fpsrpn;
|
||||||
|
|
||||||
const
|
const
|
||||||
{ RECORD IDs which didn't change across versions 2-8 }
|
{ RECORD IDs which didn't change across versions 2-8 }
|
||||||
@ -349,6 +349,21 @@ type
|
|||||||
RecordSize: Word;
|
RecordSize: Word;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{TsBIFFDefinedName }
|
||||||
|
TsBIFFDefinedName = class
|
||||||
|
private
|
||||||
|
FName: String;
|
||||||
|
FFormula: TsRPNFormula;
|
||||||
|
FValidOnSheet: Integer;
|
||||||
|
function GetRanges: TsCellRange3dArray;
|
||||||
|
public
|
||||||
|
constructor Create(AName: String; AFormula: TsRPNFormula; AValidOnSheet: Integer);
|
||||||
|
procedure UpdateSheetIndex(ASheetName: String; ASheetIndex: Integer);
|
||||||
|
property Name: String read FName;
|
||||||
|
property Ranges: TsCellRange3dArray read GetRanges;
|
||||||
|
property ValidOnSheet: Integer read FValidOnSheet;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TsSpreadBIFFReader }
|
{ TsSpreadBIFFReader }
|
||||||
TsSpreadBIFFReader = class(TsCustomSpreadReader)
|
TsSpreadBIFFReader = class(TsCustomSpreadReader)
|
||||||
protected
|
protected
|
||||||
@ -360,9 +375,11 @@ type
|
|||||||
FIncompleteNoteLength: Word;
|
FIncompleteNoteLength: Word;
|
||||||
FFirstNumFormatIndexInFile: Integer;
|
FFirstNumFormatIndexInFile: Integer;
|
||||||
FPalette: TsPalette;
|
FPalette: TsPalette;
|
||||||
|
FDefinedNames: TFPList;
|
||||||
FWorksheetNames: TStrings;
|
FWorksheetNames: TStrings;
|
||||||
FCurrentWorksheet: Integer;
|
FCurSheetIndex: Integer;
|
||||||
FActivePane: Integer;
|
FActivePane: Integer;
|
||||||
|
FExternSheets: TStrings;
|
||||||
|
|
||||||
procedure AddBuiltinNumFormats; override;
|
procedure AddBuiltinNumFormats; override;
|
||||||
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
|
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
|
||||||
@ -375,7 +392,11 @@ type
|
|||||||
// Returns the numberformat for a given XF record
|
// Returns the numberformat for a given XF record
|
||||||
procedure ExtractNumberFormat(AXFIndex: WORD;
|
procedure ExtractNumberFormat(AXFIndex: WORD;
|
||||||
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual;
|
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual;
|
||||||
|
procedure ExtractPrintRanges(AWorksheet: TsWorksheet);
|
||||||
|
procedure ExtractPrintTitles(AWorksheet: TsWorksheet);
|
||||||
|
function FindDefinedName(AWorksheet: TsWorksheet; const AName: String): TsBiffDefinedName;
|
||||||
procedure FixColors;
|
procedure FixColors;
|
||||||
|
procedure FixDefinedNames(AWorksheet: TsWorksheet);
|
||||||
function FixFontIndex(AFontIndex: Integer): Integer;
|
function FixFontIndex(AFontIndex: Integer): Integer;
|
||||||
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
|
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
|
||||||
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
|
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
|
||||||
@ -397,6 +418,8 @@ type
|
|||||||
procedure ReadDefColWidth(AStream: TStream);
|
procedure ReadDefColWidth(AStream: TStream);
|
||||||
// Read the default row height
|
// Read the default row height
|
||||||
procedure ReadDefRowHeight(AStream: TStream);
|
procedure ReadDefRowHeight(AStream: TStream);
|
||||||
|
// Read an EXTERNSHEET record (defined names)
|
||||||
|
procedure ReadExternSheet(AStream: TStream);
|
||||||
// Read FORMAT record (cell formatting)
|
// Read FORMAT record (cell formatting)
|
||||||
procedure ReadFormat(AStream: TStream); virtual;
|
procedure ReadFormat(AStream: TStream); virtual;
|
||||||
// Read FORMULA record
|
// Read FORMULA record
|
||||||
@ -431,13 +454,17 @@ type
|
|||||||
out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); virtual;
|
out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); virtual;
|
||||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); virtual;
|
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); virtual;
|
||||||
|
function ReadRPNCellRange3D(AStream: TStream; var ARPNItem: PRPNItem): Boolean; virtual;
|
||||||
procedure ReadRPNCellRangeOffset(AStream: TStream;
|
procedure ReadRPNCellRangeOffset(AStream: TStream;
|
||||||
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
|
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
|
||||||
out AFlags: TsRelFlags); virtual;
|
out AFlags: TsRelFlags); virtual;
|
||||||
function ReadRPNFunc(AStream: TStream): Word; virtual;
|
function ReadRPNFunc(AStream: TStream): Word; virtual;
|
||||||
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); virtual;
|
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); virtual;
|
||||||
function ReadRPNTokenArray(AStream: TStream; ACell: PCell;
|
function ReadRPNTokenArray(AStream: TStream; ACell: PCell;
|
||||||
ASharedFormulaBase: PCell = nil): Boolean;
|
ASharedFormulaBase: PCell = nil): Boolean; overload;
|
||||||
|
function ReadRPNTokenArray(AStream: TStream; ARpnTokenArraySize: Word;
|
||||||
|
out ARpnFormula: TsRPNFormula; ACell: PCell = nil;
|
||||||
|
ASharedFormulaBase: PCell = nil): Boolean; overload;
|
||||||
function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
|
function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
|
||||||
procedure ReadSELECTION(AStream: TStream);
|
procedure ReadSELECTION(AStream: TStream);
|
||||||
procedure ReadSharedFormula(AStream: TStream);
|
procedure ReadSharedFormula(AStream: TStream);
|
||||||
@ -599,7 +626,8 @@ implementation
|
|||||||
uses
|
uses
|
||||||
AVL_Tree, Math, Variants,
|
AVL_Tree, Math, Variants,
|
||||||
{%H-}fpspatches, fpsStrings, fpsClasses, fpsNumFormat, xlsConst,
|
{%H-}fpspatches, fpsStrings, fpsClasses, fpsNumFormat, xlsConst,
|
||||||
fpsrpn, fpsExprParser;
|
//fpsrpn,
|
||||||
|
fpsExprParser;
|
||||||
|
|
||||||
const
|
const
|
||||||
{ Helper table for rpn formulas:
|
{ Helper table for rpn formulas:
|
||||||
@ -610,6 +638,8 @@ const
|
|||||||
INT_EXCEL_TOKEN_TREFR, {fekCellRef}
|
INT_EXCEL_TOKEN_TREFR, {fekCellRef}
|
||||||
INT_EXCEL_TOKEN_TAREA_R, {fekCellRange}
|
INT_EXCEL_TOKEN_TAREA_R, {fekCellRange}
|
||||||
INT_EXCEL_TOKEN_TREFN_V, {fekCellOffset}
|
INT_EXCEL_TOKEN_TREFN_V, {fekCellOffset}
|
||||||
|
INT_EXCEL_TOKEN_TREF3D_R, {fekCellRef3d }
|
||||||
|
INT_EXCEL_TOKEN_TAREA3D_R, {fekCellRange3d }
|
||||||
INT_EXCEL_TOKEN_TNUM, {fekNum}
|
INT_EXCEL_TOKEN_TNUM, {fekNum}
|
||||||
INT_EXCEL_TOKEN_TINT, {fekInteger}
|
INT_EXCEL_TOKEN_TINT, {fekInteger}
|
||||||
INT_EXCEL_TOKEN_TSTR, {fekString}
|
INT_EXCEL_TOKEN_TSTR, {fekString}
|
||||||
@ -633,6 +663,7 @@ const
|
|||||||
INT_EXCEL_TOKEN_TLT, {fekLess <}
|
INT_EXCEL_TOKEN_TLT, {fekLess <}
|
||||||
INT_EXCEL_TOKEN_TLE, {fekLessEqual, <=}
|
INT_EXCEL_TOKEN_TLE, {fekLessEqual, <=}
|
||||||
INT_EXCEL_TOKEN_TNE, {fekNotEqual, <>}
|
INT_EXCEL_TOKEN_TNE, {fekNotEqual, <>}
|
||||||
|
INT_EXCEL_TOKEN_TLIST, {List operator (",")}
|
||||||
INT_EXCEL_TOKEN_TPAREN, {Operator in parenthesis}
|
INT_EXCEL_TOKEN_TPAREN, {Operator in parenthesis}
|
||||||
Word(-1) {fekFunc}
|
Word(-1) {fekFunc}
|
||||||
);
|
);
|
||||||
@ -820,6 +851,76 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsBIFFDefinedName }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
constructor TsBIFFDefinedName.Create(AName: String; AFormula: TsRPNFormula;
|
||||||
|
AValidOnSheet: Integer);
|
||||||
|
begin
|
||||||
|
FName := AName;
|
||||||
|
FFormula := AFormula;
|
||||||
|
FValidOnSheet := AValidOnSheet;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFFDefinedName.GetRanges: TsCellRange3dArray;
|
||||||
|
var
|
||||||
|
i, n: Integer;
|
||||||
|
elem: TsFormulaElement;
|
||||||
|
begin
|
||||||
|
SetLength(Result, 0);
|
||||||
|
for i:=0 to Length(FFormula)-1 do begin
|
||||||
|
n := Length(Result);
|
||||||
|
elem := FFormula[i];
|
||||||
|
case elem.ElementKind of
|
||||||
|
fekCellRef3D:
|
||||||
|
begin
|
||||||
|
SetLength(Result, n+1);
|
||||||
|
Result[n].Sheet1 := elem.Sheet;
|
||||||
|
Result[n].Row1 := elem.Row;
|
||||||
|
Result[n].Col1 := elem.Col;
|
||||||
|
Result[n].Sheet2 := -1;
|
||||||
|
Result[n].Row2 := Cardinal(-1);
|
||||||
|
Result[n].Col2 := Cardinal(-1);
|
||||||
|
end;
|
||||||
|
fekCellRange3d:
|
||||||
|
begin
|
||||||
|
SetLength(Result, n+1);
|
||||||
|
Result[n].Sheet1 := elem.Sheet;
|
||||||
|
Result[n].Row1 := elem.Row;
|
||||||
|
Result[n].Col1 := elem.Col;
|
||||||
|
Result[n].Sheet2 := elem.Sheet2;
|
||||||
|
Result[n].Row2 := elem.Row2;
|
||||||
|
Result[n].Col2 := elem.Col2;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsBIFFDefinedName.UpdateSheetIndex(ASheetName: String; ASheetIndex: Integer);
|
||||||
|
var
|
||||||
|
elem: TsFormulaElement;
|
||||||
|
i, p: Integer;
|
||||||
|
s: String;
|
||||||
|
begin
|
||||||
|
for i:=0 to Length(FFormula)-1 do begin
|
||||||
|
elem := FFormula[i];
|
||||||
|
if (elem.ElementKind in [fekCellRef3d, fekCellRange3d]) then begin
|
||||||
|
if elem.SheetNames = '' then
|
||||||
|
Continue;
|
||||||
|
p := pos(#9, elem.SheetNames);
|
||||||
|
if p > 0 then begin
|
||||||
|
if ASheetName = Copy(elem.SheetNames, 1, p-1) then
|
||||||
|
elem.Sheet := ASheetIndex;
|
||||||
|
if ASheetName = Copy(elem.SheetNames, p+1, MaxInt) then
|
||||||
|
elem.Sheet2 := ASheetIndex;
|
||||||
|
end else
|
||||||
|
if ASheetName = elem.SheetNames then
|
||||||
|
elem.Sheet := ASheetIndex;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
{ TsSpreadBIFFReader }
|
{ TsSpreadBIFFReader }
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
@ -834,6 +935,9 @@ begin
|
|||||||
FCellFormatList := TsCellFormatList.Create(true);
|
FCellFormatList := TsCellFormatList.Create(true);
|
||||||
// true = allow duplicates! XF indexes get out of sync if not all format records are in list
|
// 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
|
// Initial base date in case it won't be read from file
|
||||||
FDateMode := dm1900;
|
FDateMode := dm1900;
|
||||||
|
|
||||||
@ -850,8 +954,15 @@ end;
|
|||||||
Destructor of the reader class
|
Destructor of the reader class
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
destructor TsSpreadBIFFReader.Destroy;
|
destructor TsSpreadBIFFReader.Destroy;
|
||||||
|
var
|
||||||
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
|
for j:=0 to FDefinedNames.Count-1 do TObject(FDefinedNames[j]).Free;
|
||||||
|
FDefinedNames.Free;
|
||||||
|
|
||||||
|
FExternSheets.Free;
|
||||||
FPalette.Free;
|
FPalette.Free;
|
||||||
|
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -982,6 +1093,64 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadBiffReader.ExtractPrintRanges(AWorksheet: TsWorksheet);
|
||||||
|
var
|
||||||
|
defName: TsBiffDefinedName;
|
||||||
|
rng: TsCellRange3DArray;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
// #6 is the symbol for "Print_Area"
|
||||||
|
defName := FindDefinedName(AWorksheet, #6);
|
||||||
|
if defName <> nil then
|
||||||
|
begin
|
||||||
|
rng := defName.Ranges;
|
||||||
|
for i := 0 to High(rng) do
|
||||||
|
AWorksheet.AddPrintRange(rng[i].Row1, rng[i].Col1, rng[i].Row2, rng[i].Col2);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadBiffReader.ExtractPrintTitles(AWorksheet: TsWorksheet);
|
||||||
|
var
|
||||||
|
defName: TsBiffDefinedName;
|
||||||
|
rng: TsCellRange3dArray;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
// #7 is the symbol for "Print_Titles"
|
||||||
|
defName := FindDefinedName(AWorksheet, #7);
|
||||||
|
if defName <> nil then
|
||||||
|
begin
|
||||||
|
rng := defName.Ranges;
|
||||||
|
for i := 0 to High(rng) do
|
||||||
|
begin
|
||||||
|
if (rng[i].Col2 <> Cardinal(-1)) then
|
||||||
|
AWorksheet.SetRepeatedPrintCols(rng[i].Col1, rng[i].Col2)
|
||||||
|
else
|
||||||
|
if (rng[i].Row2 <> Cardinal(-1)) then
|
||||||
|
AWorksheet.SetRepeatedPrintRows(rng[i].Row1, rng[i].Row2);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsSpreadBIffReader.FindDefinedName(AWorksheet: TsWorksheet;
|
||||||
|
const AName: String): TsBiffDefinedName;
|
||||||
|
var
|
||||||
|
i: integer;
|
||||||
|
wi: Integer;
|
||||||
|
defName: TsBiffDefinedName;
|
||||||
|
begin
|
||||||
|
wi := FWorkbook.GetWorksheetIndex(AWorksheet);
|
||||||
|
for i := 0 to FDefinedNames.Count-1 do
|
||||||
|
begin
|
||||||
|
defName := TsBiffDefinedName(FDefinedNames[i]);
|
||||||
|
if (defName.ValidOnSheet = wi) and (defName.Name = AName) then
|
||||||
|
begin
|
||||||
|
Result := TsBiffDefinedName(FDefinedNames[i]);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
It is a problem of the biff file structure that the font is loaded before the
|
It is a problem of the biff file structure that the font is loaded before the
|
||||||
palette. Therefore, when reading the font, we cannot determine its rgb color.
|
palette. Therefore, when reading the font, we cannot determine its rgb color.
|
||||||
@ -1029,6 +1198,21 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadBIFFReader.FixDefinedNames(AWorksheet: TsWorksheet);
|
||||||
|
var
|
||||||
|
sheetName1, sheetName2: String;
|
||||||
|
i: Integer;
|
||||||
|
defname: TsBiffDefinedName;
|
||||||
|
sheetIndex: Integer;
|
||||||
|
begin
|
||||||
|
sheetIndex := FWorkbook.GetWorksheetIndex(AWorksheet);
|
||||||
|
for i:=0 to FDefinedNames.Count-1 do begin
|
||||||
|
defname := TsBiffDefinedName(FDefinedNames.Items[i]);
|
||||||
|
defname.UpdateSheetIndex(AWorksheet.Name, sheetIndex);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Converts the index of a font in the reader fontlist to the index of this font
|
Converts the index of a font in the reader fontlist to the index of this font
|
||||||
in the workbook's fontlist. If the font is not yet contained in the workbook
|
in the workbook's fontlist. If the font is not yet contained in the workbook
|
||||||
@ -1349,6 +1533,30 @@ begin
|
|||||||
FWorksheet.DefaultRowHeight := h - ROW_HEIGHT_CORRECTION;
|
FWorksheet.DefaultRowHeight := h - ROW_HEIGHT_CORRECTION;
|
||||||
end;
|
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.
|
||||||
|
|
||||||
|
NOTE: A character #03 is prepended to the sheet name if the EXTERNSHEET stores
|
||||||
|
a reference to one of the own sheets.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFFReader.ReadExternSheet(AStream: TStream);
|
||||||
|
var
|
||||||
|
len, b: Byte;
|
||||||
|
ansistr: AnsiString;
|
||||||
|
s: String;
|
||||||
|
begin
|
||||||
|
len := AStream.ReadByte;
|
||||||
|
b := AStream.ReadByte;
|
||||||
|
if b = 3 then
|
||||||
|
inc(len);
|
||||||
|
SetLength(ansistr, len);
|
||||||
|
AStream.ReadBuffer(ansistr[2], len-1);
|
||||||
|
ansistr[1] := char(b);
|
||||||
|
s := ConvertEncoding(ansistr, FCodePage, encodingUTF8);
|
||||||
|
FExternSheets.Add(s);
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Reads the (number) FORMAT record for formatting numerical data
|
Reads the (number) FORMAT record for formatting numerical data
|
||||||
To be overridden by descendants.
|
To be overridden by descendants.
|
||||||
@ -1957,6 +2165,13 @@ begin
|
|||||||
if (r2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
|
if (r2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TsSpreadBIFFReader.ReadRPNCellRange3D(AStream: TStream;
|
||||||
|
var ARPNItem: PRPNItem): Boolean;
|
||||||
|
begin
|
||||||
|
Result := false; // "false" means: "not supported"
|
||||||
|
// must be overridden
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Reads the difference between row and column corner indexes of a cell range
|
Reads the difference between row and column corner indexes of a cell range
|
||||||
and a reference cell.
|
and a reference cell.
|
||||||
@ -2024,6 +2239,7 @@ end;
|
|||||||
Reads the array of rpn tokens from the current stream position, creates an
|
Reads the array of rpn tokens from the current stream position, creates an
|
||||||
rpn formula, converts it to a string formula and stores it in the cell.
|
rpn formula, converts it to a string formula and stores it in the cell.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
|
(*
|
||||||
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
|
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
|
||||||
ACell: PCell; ASharedFormulaBase: PCell = nil): Boolean;
|
ACell: PCell; ASharedFormulaBase: PCell = nil): Boolean;
|
||||||
var
|
var
|
||||||
@ -2179,6 +2395,174 @@ begin
|
|||||||
Result := true;
|
Result := true;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
*)
|
||||||
|
|
||||||
|
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
|
||||||
|
ACell: PCell; ASharedFormulaBase: PCell = nil): Boolean;
|
||||||
|
var
|
||||||
|
n: Word;
|
||||||
|
rpnFormula: TsRPNformula;
|
||||||
|
strFormula: String;
|
||||||
|
begin
|
||||||
|
n := ReadRPNTokenArraySize(AStream);
|
||||||
|
Result := ReadRPNTokenArray(AStream, n, rpnFormula, ACell, ASharedFormulaBase);
|
||||||
|
if Result then begin
|
||||||
|
strFormula := FWorksheet.ConvertRPNFormulaToStringFormula(rpnFormula);
|
||||||
|
if strFormula <> '' then
|
||||||
|
ACell^.FormulaValue := strFormula;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
|
||||||
|
ARpnTokenArraySize: Word; out ARpnFormula: TsRPNFormula; ACell: PCell = nil;
|
||||||
|
ASharedFormulaBase: PCell = nil): Boolean;
|
||||||
|
var
|
||||||
|
n: Word;
|
||||||
|
p0: Int64;
|
||||||
|
token: Byte;
|
||||||
|
rpnItem: PRPNItem;
|
||||||
|
supported: boolean;
|
||||||
|
dblVal: Double = 0.0; // IEEE 8 byte floating point number
|
||||||
|
flags: TsRelFlags;
|
||||||
|
r, c, r2, c2: Cardinal;
|
||||||
|
dr, dc, dr2, dc2: Integer;
|
||||||
|
sheetIndex: Integer;
|
||||||
|
fek: TFEKind;
|
||||||
|
exprDef: TsBuiltInExprIdentifierDef;
|
||||||
|
funcCode: Word;
|
||||||
|
b: Byte;
|
||||||
|
found: Boolean;
|
||||||
|
begin
|
||||||
|
rpnItem := nil;
|
||||||
|
p0 := AStream.Position;
|
||||||
|
supported := true;
|
||||||
|
while (AStream.Position < p0 + ARPNTokenArraySize) and supported do begin
|
||||||
|
token := AStream.ReadByte;
|
||||||
|
case token of
|
||||||
|
INT_EXCEL_TOKEN_TREFV:
|
||||||
|
begin
|
||||||
|
ReadRPNCellAddress(AStream, r, c, flags);
|
||||||
|
rpnItem := RPNCellValue(r, c, flags, rpnItem);
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TREFR:
|
||||||
|
begin
|
||||||
|
ReadRPNCellAddress(AStream, r, c, flags);
|
||||||
|
rpnItem := RPNCellRef(r, c, flags, rpnItem);
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TAREA_R, INT_EXCEL_TOKEN_TAREA_V:
|
||||||
|
begin
|
||||||
|
ReadRPNCellRangeAddress(AStream, r, c, r2, c2, flags);
|
||||||
|
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TREFN_R, INT_EXCEL_TOKEN_TREFN_V:
|
||||||
|
begin
|
||||||
|
ReadRPNCellAddressOffset(AStream, dr, dc, flags);
|
||||||
|
// For compatibility with other formats, convert offsets back to regular indexes.
|
||||||
|
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_TREFN_V: rpnItem := RPNCellValue(r, c, flags, rpnItem);
|
||||||
|
INT_EXCEL_TOKEN_TREFN_R: rpnItem := RPNCellRef(r, c, flags, rpnItem);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TAREA3D_R:
|
||||||
|
begin
|
||||||
|
if not ReadRPNCellRange3D(AStream, rpnItem) then supported := false;
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TREFN_A:
|
||||||
|
begin
|
||||||
|
ReadRPNCellRangeOffset(AStream, dr, dc, dr2, dc2, flags);
|
||||||
|
// For compatibility with other formats, convert offsets back to regular indexes.
|
||||||
|
if (rfRelRow in flags)
|
||||||
|
then r := LongInt(ACell^.Row) + dr
|
||||||
|
else r := LongInt(ASharedFormulaBase^.Row) + dr;
|
||||||
|
if (rfRelRow2 in flags)
|
||||||
|
then r2 := LongInt(ACell^.Row) + dr2
|
||||||
|
else r2 := LongInt(ASharedFormulaBase^.Row) + dr2;
|
||||||
|
if (rfRelCol in flags)
|
||||||
|
then c := LongInt(ACell^.Col) + dc
|
||||||
|
else c := LongInt(ASharedFormulaBase^.Col) + dc;
|
||||||
|
if (rfRelCol2 in flags)
|
||||||
|
then c2 := LongInt(ACell^.Col) + dc2
|
||||||
|
else c2 := LongInt(ASharedFormulaBase^.Col) + dc2;
|
||||||
|
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TMISSARG:
|
||||||
|
rpnItem := RPNMissingArg(rpnItem);
|
||||||
|
INT_EXCEL_TOKEN_TSTR:
|
||||||
|
rpnItem := RPNString(ReadString_8BitLen(AStream), rpnItem);
|
||||||
|
INT_EXCEL_TOKEN_TERR:
|
||||||
|
rpnItem := RPNErr(ConvertFromExcelError(AStream.ReadByte), rpnItem);
|
||||||
|
INT_EXCEL_TOKEN_TBOOL:
|
||||||
|
rpnItem := RPNBool(AStream.ReadByte=1, rpnItem);
|
||||||
|
INT_EXCEL_TOKEN_TINT:
|
||||||
|
rpnItem := RPNInteger(WordLEToN(AStream.ReadWord), rpnItem);
|
||||||
|
INT_EXCEL_TOKEN_TNUM:
|
||||||
|
begin
|
||||||
|
AStream.ReadBuffer(dblVal, 8);
|
||||||
|
rpnItem := RPNNumber(dblVal, rpnItem);
|
||||||
|
end;
|
||||||
|
INT_EXCEL_TOKEN_TPAREN:
|
||||||
|
rpnItem := RPNParenthesis(rpnItem);
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_FUNC_R,
|
||||||
|
INT_EXCEL_TOKEN_FUNC_V,
|
||||||
|
INT_EXCEL_TOKEN_FUNC_A:
|
||||||
|
// functions with fixed argument count
|
||||||
|
begin
|
||||||
|
funcCode := ReadRPNFunc(AStream);
|
||||||
|
exprDef := BuiltInIdentifiers.IdentifierByExcelCode(funcCode);
|
||||||
|
if exprDef <> nil then
|
||||||
|
rpnItem := RPNFunc(exprDef.Name, rpnItem)
|
||||||
|
else
|
||||||
|
supported := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_FUNCVAR_R,
|
||||||
|
INT_EXCEL_TOKEN_FUNCVAR_V,
|
||||||
|
INT_EXCEL_TOKEN_FUNCVAR_A:
|
||||||
|
// functions with variable argument count
|
||||||
|
begin
|
||||||
|
b := AStream.ReadByte;
|
||||||
|
funcCode := ReadRPNFunc(AStream);
|
||||||
|
exprDef := BuiltinIdentifiers.IdentifierByExcelCode(funcCode);
|
||||||
|
if exprDef <> nil then
|
||||||
|
rpnItem := RPNFunc(exprDef.Name, b, rpnItem)
|
||||||
|
else
|
||||||
|
supported := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TEXP:
|
||||||
|
// Indicates that cell belongs to a shared or array formula.
|
||||||
|
// This information is not needed any more.
|
||||||
|
ReadRPNSharedFormulaBase(AStream, r, c);
|
||||||
|
|
||||||
|
else
|
||||||
|
found := false;
|
||||||
|
for fek in TBasicOperationTokens do
|
||||||
|
if (TokenIDs[fek] = token) then begin
|
||||||
|
rpnItem := RPNFunc(fek, rpnItem);
|
||||||
|
found := true;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
if not found then
|
||||||
|
supported := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if not supported then begin
|
||||||
|
DestroyRPNFormula(rpnItem);
|
||||||
|
ARPNFormula := nil;
|
||||||
|
Result := false;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
ARPNFormula := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items!
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Helper function for reading of the size of the token array of an RPN formula.
|
Helper function for reading of the size of the token array of an RPN formula.
|
||||||
@ -2385,19 +2769,9 @@ end;
|
|||||||
procedure TsSpreadBIFFReader.InternalReadFromStream(AStream: TStream);
|
procedure TsSpreadBIFFReader.InternalReadFromStream(AStream: TStream);
|
||||||
var
|
var
|
||||||
BIFFEOF: Boolean;
|
BIFFEOF: Boolean;
|
||||||
|
i: Integer;
|
||||||
|
sheet: TsWorksheet;
|
||||||
begin
|
begin
|
||||||
{ OLEStream := TMemoryStream.Create;
|
|
||||||
try
|
|
||||||
OLEStorage := TOLEStorage.Create;
|
|
||||||
try
|
|
||||||
// Only one stream is necessary for any number of worksheets
|
|
||||||
OLEDocument.Stream := AStream; //OLEStream;
|
|
||||||
OLEStorage.ReadOLEStream(AStream, OLEDocument, AStreamName);
|
|
||||||
finally
|
|
||||||
OLEStorage.Free;
|
|
||||||
end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the operation succeeded
|
// Check if the operation succeeded
|
||||||
if AStream.Size = 0 then
|
if AStream.Size = 0 then
|
||||||
raise Exception.Create('[TsSpreadBIFFReader.InternalReadFromStream] Reading of OLE document failed');
|
raise Exception.Create('[TsSpreadBIFFReader.InternalReadFromStream] Reading of OLE document failed');
|
||||||
@ -2408,7 +2782,7 @@ begin
|
|||||||
{Initializations }
|
{Initializations }
|
||||||
FWorksheetNames := TStringList.Create;
|
FWorksheetNames := TStringList.Create;
|
||||||
try
|
try
|
||||||
FCurrentWorksheet := 0;
|
FCurSheetIndex := 0;
|
||||||
BIFFEOF := false;
|
BIFFEOF := false;
|
||||||
|
|
||||||
{ Read workbook globals }
|
{ Read workbook globals }
|
||||||
@ -2428,12 +2802,21 @@ begin
|
|||||||
BIFFEOF := true;
|
BIFFEOF := true;
|
||||||
|
|
||||||
// Final preparations
|
// Final preparations
|
||||||
inc(FCurrentWorksheet);
|
inc(FCurSheetIndex);
|
||||||
// It can happen in files written by Office97 that the OLE directory is
|
// It can happen in files written by Office97 that the OLE directory is
|
||||||
// at the end of the file.
|
// at the end of the file.
|
||||||
if FCurrentWorksheet = FWorksheetNames.Count then
|
if FCurSheetIndex = FWorksheetNames.Count then
|
||||||
BIFFEOF := true;
|
BIFFEOF := true;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Extract print ranges, repeated rows/cols }
|
||||||
|
for i:=0 to FWorkbook.GetWorksheetCount-1 do begin
|
||||||
|
sheet := FWorkbook.GetWorksheetByIndex(i);
|
||||||
|
FixDefinedNames(sheet);
|
||||||
|
ExtractPrintRanges(sheet);
|
||||||
|
ExtractPrintTitles(sheet);
|
||||||
|
end;
|
||||||
|
|
||||||
finally
|
finally
|
||||||
{ Finalization }
|
{ Finalization }
|
||||||
FreeAndNil(FWorksheetNames);
|
FreeAndNil(FWorksheetNames);
|
||||||
|
@ -55,6 +55,12 @@ const
|
|||||||
INT_EXCEL_TOKEN_TAREAN_R = $2D;
|
INT_EXCEL_TOKEN_TAREAN_R = $2D;
|
||||||
INT_EXCEL_TOKEN_TAREAN_V = $4D;
|
INT_EXCEL_TOKEN_TAREAN_V = $4D;
|
||||||
INT_EXCEL_TOKEN_TAREAN_A = $6D;
|
INT_EXCEL_TOKEN_TAREAN_A = $6D;
|
||||||
|
INT_EXCEL_TOKEN_TREF3d_R = $3A;
|
||||||
|
INT_EXCEL_TOKEN_TREF3d_V = $5A;
|
||||||
|
INT_EXCEL_TOKEN_TREF3d_A = $7A;
|
||||||
|
INT_EXCEL_TOKEN_TAREA3d_R = $3B;
|
||||||
|
INT_EXCEL_TOKEN_TAREA3d_V = $5B;
|
||||||
|
INT_EXCEL_TOKEN_TAREA3d_A = $7B;
|
||||||
|
|
||||||
{ Function Tokens }
|
{ Function Tokens }
|
||||||
// _R: reference; _V: value; _A: array
|
// _R: reference; _V: value; _A: array
|
||||||
|
Reference in New Issue
Block a user