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:
wp_xxyyzz
2016-02-19 19:53:10 +00:00
parent 0b9b04a541
commit d2b80cf143
7 changed files with 921 additions and 78 deletions

View File

@ -51,6 +51,10 @@ function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
ANext: PRPNItem): PRPNItem; overload;
function RPNCellOffset(ARowOffset, AColOffset: Integer; AFlags: TsRelFlags;
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 RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
function RPNMissingArg(ANext: PRPNItem): PRPNItem;
@ -82,6 +86,7 @@ begin
New(Result);
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
Result^.FE.StringValue := '';
Result^.FE.SheetNames := '';
end;
{@@ ----------------------------------------------------------------------------
@ -254,6 +259,30 @@ begin
Result^.Next := ANext;
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.
@ -437,6 +466,7 @@ begin
nextitem := item^.Next;
Result[n] := item^.FE;
Result[n].StringValue := item^.FE.StringValue;
Result[n].Sheetnames := item^.FE.SheetNames;
if AReverse then dec(n) else inc(n);
DisposeRPNItem(item);
item := nextitem;

View File

@ -104,13 +104,15 @@ type
}
TFEKind = (
{ Basic operands }
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
fekString, fekBool, fekErr, fekMissingArg,
fekCell, fekCellRef, fekCellRange, fekCellOffset,
fekCellRef3d, fekCellRange3d,
fekNum, fekInteger, fekString, fekBool, fekErr, fekMissingArg,
{ Basic operations }
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
fekConcat, // string concatenation
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 }
fekFunc
);
@ -139,16 +141,17 @@ const
type
{@@ Elements of an expanded formula.
Note: If ElementKind is fekCellOffset, "Row" and "Col" have to be cast
to signed integers! }
Note: If ElementKind is fekCellOffset, "Row" and "Col" have to be cast to signed integers! }
TsFormulaElement = record
ElementKind: TFEKind;
Row, Row2: 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;
IntValue: Word;
StringValue: String;
RelFlags: TsRelFlags; // store info on relative/absolute addresses
RelFlags: TsRelFlags; // info on relative/absolute addresses
FuncName: String;
ParamsNum: Byte;
end;
@ -569,6 +572,15 @@ type
{@@ Array with cell ranges }
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 }
TsRowColRange = record
FirstIndex, LastIndex: Cardinal;

View File

@ -115,6 +115,25 @@ type
procedure TestWriteRead_BIFF5_HeaderFooterFontColor_2sheets;
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 }
procedure TestWriteRead_BIFF8_PageMargins_1sheet_0;
procedure TestWriteRead_BIFF8_PageMargins_1sheet_1;
@ -169,6 +188,25 @@ type
procedure TestWriteRead_BIFF8_HeaderFooterFontColor_2sheets;
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 }
procedure TestWriteRead_OOXML_PageMargins_1sheet_0;
procedure TestWriteRead_OOXML_PageMargins_1sheet_1;
@ -1138,6 +1176,87 @@ begin
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 }
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PageMargins_1sheet_0;
@ -1363,6 +1482,87 @@ begin
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 }
procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_PageMargins_1sheet_0;

View File

@ -59,7 +59,7 @@ interface
uses
Classes, SysUtils, fpcanvas, lconvencoding,
fpsTypes, fpspreadsheet,
fpsTypes, fpspreadsheet, fpsrpn,
xlscommon,
{$ifdef USE_NEW_OLE}
fpolebasic,
@ -69,7 +69,6 @@ uses
fpsUtils;
type
{ TsSpreadBIFF5Reader }
TsSpreadBIFF5Reader = class(TsSpreadBIFFReader)
@ -77,9 +76,11 @@ type
procedure PopulatePalette; override;
{ Record writing methods }
procedure ReadBoundsheet(AStream: TStream);
procedure ReadDEFINEDNAME(AStream: TStream);
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 ReadRSTRING(AStream: TStream);
procedure ReadStandardWidth(AStream: TStream; ASheet: TsWorksheet);
procedure ReadStringRecord(AStream: TStream); override;
@ -88,7 +89,6 @@ type
procedure ReadXF(AStream: TStream);
public
{ General reading methods }
// procedure ReadFromFile(AFileName: string); override;
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
end;
@ -346,7 +346,9 @@ type
end;
{------------------------------------------------------------------------------}
{ TsSpreadBIFF5Reader }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Populates the reader's default palette using the BIFF5 default colors.
@ -357,6 +359,96 @@ begin
FPalette.UseColors(PALETTE_BIFF5);
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);
var
SectionEOF: Boolean = False;
@ -375,6 +467,8 @@ begin
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
INT_EXCEL_ID_DEFINEDNAME : ReadDefinedName(AStream);
INT_EXCEL_ID_EXTERNSHEET : ReadExternSheet(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
INT_EXCEL_ID_XF : ReadXF(AStream);
@ -401,7 +495,7 @@ var
RecordType: Word;
CurStreamPos: Int64;
begin
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurrentWorksheet], true);
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurSheetIndex], true);
while (not SectionEOF) do
begin
@ -497,30 +591,36 @@ begin
FixRows(FWorksheet);
end;
procedure TsSpreadBIFF5Reader.ReadBoundsheet(AStream: TStream);
function TsSpreadBIFF5Reader.ReadRPNCellRange3D(AStream: TStream;
var ARPNItem: PRPNItem): Boolean;
var
Len: Byte;
s: AnsiString;
sheetName: String;
sheetIndex: SmallInt;
sheetIndex1, sheetIndex2: Word;
r1, c1, r2, c2: Cardinal;
flags: TsRelFlags;
begin
{ Absolute stream position of the BOF record of the sheet represented
by this record }
// Just assume that they are in order
AStream.ReadDWord();
Result := true;
{ Visibility }
AStream.ReadByte();
// 1-based index to EXTERNSHEET record containing name of first referenced worksheet
// 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 }
AStream.ReadByte();
// Next 8 bytes not used.
AStream.ReadDWord;
AStream.ReadDWord;
{ Sheet name: Byte string, 8-bit length }
Len := AStream.ReadByte();
// Zero-based index to first and last referenced sheet (in case of internal ref)
sheetIndex1 := WordLEToN(AStream.ReadWord);
sheetIndex2 := WordLEToN(AStream.ReadWord);
SetLength(s, Len);
AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar));
sheetName := ConvertEncoding(s, FCodePage, EncodingUTF8);
FWorksheetNames.Add(sheetName);
// Cell range coordinates
ReadRPNCellRangeAddress(AStream, r1, c1, r2, c2, flags);
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;
procedure TsSpreadBIFF5Reader.ReadRSTRING(AStream: TStream);

View File

@ -56,7 +56,7 @@ interface
uses
Classes, SysUtils, fpcanvas, DateUtils, contnrs, lazutf8,
fpstypes, fpspreadsheet, xlscommon,
fpstypes, fpspreadsheet, fpsrpn, xlscommon,
{$ifdef USE_NEW_OLE}
fpolebasic,
{$else}
@ -66,6 +66,12 @@ uses
type
TBIFF8ExternSheet = packed record
ExternBookIndex: Word;
FirstSheetIndex: Word;
LastSheetIndex: Word;
end;
{ TsSpreadBIFF8Reader }
TsSpreadBIFF8Reader = class(TsSpreadBIFFReader)
private
@ -75,22 +81,26 @@ type
FCommentPending: Boolean;
FCommentID: Integer;
FCommentLen: Integer;
procedure ReadBoundsheet(AStream: TStream);
FBiff8ExternSheets: array of TBiff8ExternSheet;
function ReadString(const AStream: TStream; const ALength: Word;
out ARichTextParams: TsRichTextParams): String;
function ReadUnformattedWideString(const AStream: TStream;
const ALength: Word): WideString;
function ReadWideString(const AStream: TStream; const ALength: Word;
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
procedure PopulatePalette; override;
procedure ReadBOUNDSHEET(AStream: TStream);
procedure ReadCONTINUE(const AStream: TStream);
procedure ReadDEFINEDNAME(const AStream: TStream);
procedure ReadEXTERNSHEET(const AStream: TStream);
procedure ReadFONT(const AStream: TStream);
procedure ReadFORMAT(AStream: TStream); override;
procedure ReadHeaderFooter(AStream: TStream; AIsHeader: Boolean); override;
procedure ReadHyperLink(AStream: TStream);
procedure ReadHyperlinkToolTip(AStream: TStream);
procedure ReadHyperLink(const AStream: TStream);
procedure ReadHyperlinkToolTip(const AStream: TStream);
procedure ReadLABEL(AStream: TStream); override;
procedure ReadLabelSST(const AStream: TStream);
procedure ReadMergedCells(const AStream: TStream);
@ -103,6 +113,7 @@ 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;
procedure ReadRPNCellRangeOffset(AStream: TStream;
out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer;
out AFlags: TsRelFlags); override;
@ -443,13 +454,14 @@ type
Text: String;
end;
{ TsSpreadBIFF8Reader }
destructor TsSpreadBIFF8Reader.Destroy;
var
j: Integer;
begin
SetLength(FBiff8ExternSheets, 0);
if Assigned(FSharedStringTable) then
begin
for j := FSharedStringTable.Count-1 downto 0 do
@ -779,7 +791,9 @@ begin
case RecordType of
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_DEFINEDNAME : ReadDEFINEDNAME(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream);
INT_EXCEL_ID_SST : ReadSST(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodepage(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream);
@ -809,7 +823,7 @@ var
RecordType: Word;
CurStreamPos: Int64;
begin
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurrentWorksheet], true);
FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurSheetIndex], true);
while (not SectionEOF) do
begin
@ -829,6 +843,7 @@ begin
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_DEFINEDNAME : ReadDefinedName(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_FOOTER : ReadHeaderFooter(AStream, false);
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
@ -944,7 +959,6 @@ begin
end;
end;
(*
const AStrea
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream);
var
BIFF8EOF: Boolean;
@ -1179,6 +1193,26 @@ begin
if (c2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
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
and a reference cell.
Overriding the implementation in xlscommon. }
@ -1628,6 +1662,84 @@ begin
FCellFormatList.Add(fmt);
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. }
procedure TsSpreadBIFF8Reader.ReadFONT(const AStream: TStream);
var
@ -1761,7 +1873,7 @@ end;
{@@ ----------------------------------------------------------------------------
Reads a HYPERLINK record
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Reader.ReadHyperlink(AStream: TStream);
procedure TsSpreadBIFF8Reader.ReadHyperlink(const AStream: TStream);
var
row, col, row1, col1, row2, col2: word;
guid: TGUID;
@ -1902,7 +2014,7 @@ end;
{@@ ----------------------------------------------------------------------------
Reads a HYPERLINK TOOLTIP record
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Reader.ReadHyperlinkToolTip(AStream: TStream);
procedure TsSpreadBIFF8Reader.ReadHyperlinkToolTip(const AStream: TStream);
var
txt: String;
widestr: widestring;

View File

@ -12,7 +12,7 @@ interface
uses
Classes, SysUtils, DateUtils, lconvencoding,
fpsTypes, fpSpreadsheet, fpsUtils, fpsNumFormatParser, fpsPalette,
fpsReaderWriter;
fpsReaderWriter, fpsrpn;
const
{ RECORD IDs which didn't change across versions 2-8 }
@ -349,6 +349,21 @@ type
RecordSize: Word;
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 = class(TsCustomSpreadReader)
protected
@ -360,9 +375,11 @@ type
FIncompleteNoteLength: Word;
FFirstNumFormatIndexInFile: Integer;
FPalette: TsPalette;
FDefinedNames: TFPList;
FWorksheetNames: TStrings;
FCurrentWorksheet: Integer;
FCurSheetIndex: Integer;
FActivePane: Integer;
FExternSheets: TStrings;
procedure AddBuiltinNumFormats; override;
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
@ -375,7 +392,11 @@ type
// Returns the numberformat for a given XF record
procedure ExtractNumberFormat(AXFIndex: WORD;
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 FixDefinedNames(AWorksheet: TsWorksheet);
function FixFontIndex(AFontIndex: Integer): Integer;
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
@ -397,6 +418,8 @@ type
procedure ReadDefColWidth(AStream: TStream);
// Read the default row height
procedure ReadDefRowHeight(AStream: TStream);
// Read an EXTERNSHEET record (defined names)
procedure ReadExternSheet(AStream: TStream);
// Read FORMAT record (cell formatting)
procedure ReadFormat(AStream: TStream); virtual;
// Read FORMULA record
@ -431,13 +454,17 @@ 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;
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;
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;
procedure ReadSELECTION(AStream: TStream);
procedure ReadSharedFormula(AStream: TStream);
@ -599,7 +626,8 @@ implementation
uses
AVL_Tree, Math, Variants,
{%H-}fpspatches, fpsStrings, fpsClasses, fpsNumFormat, xlsConst,
fpsrpn, fpsExprParser;
//fpsrpn,
fpsExprParser;
const
{ Helper table for rpn formulas:
@ -610,6 +638,8 @@ const
INT_EXCEL_TOKEN_TREFR, {fekCellRef}
INT_EXCEL_TOKEN_TAREA_R, {fekCellRange}
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_TINT, {fekInteger}
INT_EXCEL_TOKEN_TSTR, {fekString}
@ -633,6 +663,7 @@ const
INT_EXCEL_TOKEN_TLT, {fekLess <}
INT_EXCEL_TOKEN_TLE, {fekLessEqual, <=}
INT_EXCEL_TOKEN_TNE, {fekNotEqual, <>}
INT_EXCEL_TOKEN_TLIST, {List operator (",")}
INT_EXCEL_TOKEN_TPAREN, {Operator in parenthesis}
Word(-1) {fekFunc}
);
@ -820,6 +851,76 @@ begin
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 }
{------------------------------------------------------------------------------}
@ -834,6 +935,9 @@ 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
FDateMode := dm1900;
@ -850,8 +954,15 @@ end;
Destructor of the reader class
-------------------------------------------------------------------------------}
destructor TsSpreadBIFFReader.Destroy;
var
j: Integer;
begin
for j:=0 to FDefinedNames.Count-1 do TObject(FDefinedNames[j]).Free;
FDefinedNames.Free;
FExternSheets.Free;
FPalette.Free;
inherited Destroy;
end;
@ -982,6 +1093,64 @@ begin
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
palette. Therefore, when reading the font, we cannot determine its rgb color.
@ -1029,6 +1198,21 @@ begin
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
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;
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
To be overridden by descendants.
@ -1957,6 +2165,13 @@ begin
if (r2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2);
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
and a reference cell.
@ -2024,6 +2239,7 @@ end;
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.
-------------------------------------------------------------------------------}
(*
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
ACell: PCell; ASharedFormulaBase: PCell = nil): Boolean;
var
@ -2179,6 +2395,174 @@ begin
Result := true;
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.
@ -2385,19 +2769,9 @@ end;
procedure TsSpreadBIFFReader.InternalReadFromStream(AStream: TStream);
var
BIFFEOF: Boolean;
i: Integer;
sheet: TsWorksheet;
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
if AStream.Size = 0 then
raise Exception.Create('[TsSpreadBIFFReader.InternalReadFromStream] Reading of OLE document failed');
@ -2408,7 +2782,7 @@ begin
{Initializations }
FWorksheetNames := TStringList.Create;
try
FCurrentWorksheet := 0;
FCurSheetIndex := 0;
BIFFEOF := false;
{ Read workbook globals }
@ -2428,12 +2802,21 @@ begin
BIFFEOF := true;
// Final preparations
inc(FCurrentWorksheet);
inc(FCurSheetIndex);
// It can happen in files written by Office97 that the OLE directory is
// at the end of the file.
if FCurrentWorksheet = FWorksheetNames.Count then
if FCurSheetIndex = FWorksheetNames.Count then
BIFFEOF := true;
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
{ Finalization }
FreeAndNil(FWorksheetNames);

View File

@ -55,6 +55,12 @@ const
INT_EXCEL_TOKEN_TAREAN_R = $2D;
INT_EXCEL_TOKEN_TAREAN_V = $4D;
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 }
// _R: reference; _V: value; _A: array