From 2d26e915223c0d5e5ba52a4fe7eaa089f3d0ce74 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Thu, 4 Sep 2014 13:18:28 +0000 Subject: [PATCH] fpspreadsheet: Reading of biff5 and biff8 shared formulas (biff2 does not have them). git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3524 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpspreadsheet.pas | 17 +++++ components/fpspreadsheet/xlsbiff8.pas | 35 +++++++++ components/fpspreadsheet/xlscommon.pas | 85 +++++++++++++++++++--- 3 files changed, 127 insertions(+), 10 deletions(-) diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 0dfec3745..c11f9f845 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -2677,14 +2677,17 @@ begin if ACell = nil then exit; if HasFormula(ACell) then begin + // case (1): Formula is localized and has to be converted to default syntax if ALocalized then begin parser := TsSpreadsheetParser.Create(self); try if ACell^.SharedFormulaBase <> nil then begin + // case (1a): shared formula parser.ActiveCell := ACell; parser.Expression := ACell^.SharedFormulaBase^.FormulaValue; end else begin + // case (1b): normal formula parser.ActiveCell := nil; parser.Expression := ACell^.FormulaValue; end; @@ -2694,6 +2697,20 @@ begin end; end else + // case (2): Formula is in default syntax + if ACell^.SharedFormulaBase <> nil then + begin + // case (2a): shared formula + parser := TsSpreadsheetParser.Create(self); + try + parser.ActiveCell := ACell; + parser.Expression := ACell^.SharedFormulaBase^.FormulaValue; + Result := parser.Expression; + finally + parser.Free; + end; + end else + // case (2b): normal formula Result := ACell^.FormulaValue; end; end; diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 452d810f8..5bfcb475b 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -91,6 +91,9 @@ type out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); override; procedure ReadRPNCellRangeAddress(AStream: TStream; out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override; + procedure ReadRPNCellRangeOffset(AStream: TStream; + out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer; + out AFlags: TsRelFlags); override; procedure ReadSST(const AStream: TStream); function ReadString_8bitLen(AStream: TStream): String; override; procedure ReadStringRecord(AStream: TStream); override; @@ -1710,6 +1713,38 @@ begin if (c2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2); end; +{ Reads the difference between row and column corner indexes of a cell range + and a reference cell. + Overriding the implementation in xlscommon. } +procedure TsSpreadBIFF8Reader.ReadRPNCellRangeOffset(AStream: TStream; + out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer; + out AFlags: TsRelFlags); +var + c1, c2: Word; +begin + // 2 bytes for offset of first row + ARow1Offset := ShortInt(WordLEToN(AStream.ReadWord)); + + // 2 bytes for offset to last row + ARow2Offset := ShortInt(WordLEToN(AStream.ReadWord)); + + // 2 bytes for offset of first column + c1 := WordLEToN(AStream.ReadWord); + ACol1Offset := Shortint(Lo(c1)); + + // 2 bytes for offset of last column + c2 := WordLEToN(AStream.ReadWord); + ACol2Offset := ShortInt(Lo(c2)); + + // Extract info on absolute/relative addresses. + AFlags := []; + if (c1 and MASK_EXCEL_RELATIVE_COL <> 0) then Include(AFlags, rfRelCol); + if (c1 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow); + if (c2 and MASK_EXCEL_RELATIVE_COL <> 0) then Include(AFlags, rfRelCol2); + if (c2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2); + +end; + procedure TsSpreadBIFF8Reader.ReadSST(const AStream: TStream); var Items: DWORD; diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 0b0ded825..efd2f9951 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -272,6 +272,9 @@ type out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); virtual; procedure ReadRPNCellRangeAddress(AStream: TStream; out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); 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): Boolean; @@ -1388,8 +1391,8 @@ begin if (r and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow); end; -{ Read the difference between cell row and column indexed of a cell and a reference - cell. +{ Read the difference between cell row and column indexes of a cell and a + reference cell. Implemented here for BIFF5. BIFF8 must be overridden. Not used by BIFF2. } procedure TsSpreadBIFFReader.ReadRPNCellAddressOffset(AStream: TStream; out ARowOffset, AColOffset: Integer; out AFlags: TsRelFlags); @@ -1404,7 +1407,7 @@ begin ARowOffset := dr; // 1 byte for column - dc := AStream.ReadByte; + dc := ShortInt(AStream.ReadByte); AColOffset := dc; // Extract absolute/relative flags @@ -1413,8 +1416,9 @@ begin if (r and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow); end; -{ Reads the cell address used in an RPN formula element. Evaluates the corresponding - bits to distinguish between absolute and relative addresses. +{ Reads the cell range address used in an RPN formula element. + Evaluates the corresponding bits to distinguish between absolute and + relative addresses. Implemented here for BIFF2-BIFF5. BIFF8 must be overridden. } procedure TsSpreadBIFFReader.ReadRPNCellRangeAddress(AStream: TStream; out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); @@ -1438,6 +1442,43 @@ begin if (r2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2); end; +{ Read the difference between row and column corner indexes of a cell range + and a reference cell. + Implemented here for BIFF5. BIFF8 must be overridden. Not used by BIFF2. } +procedure TsSpreadBIFFReader.ReadRPNCellRangeOffset(AStream: TStream; + out ARow1Offset, ACol1Offset, ARow2Offset, ACol2Offset: Integer; + out AFlags: TsRelFlags); +var + r1, r2: Word; + dr1, dr2: SmallInt; + dc1, dc2: ShortInt; +begin + // 2 bytes for offset to first row + r1 := WordLEToN(AStream.ReadWord); + dr1 := SmallInt(r1 and $3FFF); + ARow1Offset := dr1; + + // 2 bytes for offset to last row + r2 := WordLEToN(AStream.ReadWord); + dr2 := SmallInt(r2 and $3FFF); + ARow1Offset := dr2; + + // 1 byte for offset to first column + dc1 := ShortInt(AStream.ReadByte); + ACol1Offset := dc1; + + // 1 byte for offset to last column + dc2 := ShortInt(AStream.ReadByte); + ACol2Offset := dc2; + + // Extract absolute/relative flags + AFlags := []; + if (r1 and MASK_EXCEL_RELATIVE_COL <> 0) then Include(AFlags, rfRelCol); + if (r1 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow); + if (r2 and MASK_EXCEL_RELATIVE_COL <> 0) then Include(AFlags, rfRelCol2); + if (r2 and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow2); +end; + { Reads the identifier for an RPN function with fixed argument count. Valid for BIFF4-BIFF8. Override in BIFF2-BIFF3 which read 1 byte only. } function TsSpreadBIFFReader.ReadRPNFunc(AStream: TStream): Word; @@ -1470,7 +1511,7 @@ var dblVal: Double = 0.0; // IEEE 8 byte floating point number flags: TsRelFlags; r, c, r2, c2: Cardinal; - dr, dc: Integer; + dr, dc, dr2, dc2: Integer; fek: TFEKind; exprDef: TsBuiltInExprIdentifierDef; funcCode: Word; @@ -1503,11 +1544,35 @@ begin INT_EXCEL_TOKEN_TREFN_R, INT_EXCEL_TOKEN_TREFN_V: begin ReadRPNCellAddressOffset(AStream, dr, dc, flags); - rpnItem := RPNCellOffset(dr, dc, flags, rpnItem); + // For compatibility with other formats, convert offsets back to regular indexes. + if (rfRelRow in flags) + then r := ACell^.Row + dr + else r := ACell^.SharedFormulaBase^.Row + dr; + if (rfRelCol in flags) + then c := ACell^.Col + dc + else c := ACell^.SharedFormulaBase^.Col + 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_TREFN_A: begin - raise Exception.Create('Cell range offset not yet implemented. Please report an issue'); + ReadRPNCellRangeOffset(AStream, dr, dc, dr2, dc2, flags); + // For compatibility with other formats, convert offsets back to regular indexes. + if (rfRelRow in flags) + then r := ACell^.Row + dr + else r := ACell^.SharedFormulaBase^.Row + dr; + if (rfRelRow2 in flags) + then r2 := ACell^.Row + dr2 + else r2 := ACell^.SharedFormulaBase^.Row + dr2; + if (rfRelCol in flags) + then c := ACell^.Col + dc + else c := ACell^.SharedFormulaBase^.Col + dc; + if (rfRelCol2 in flags) + then c2 := ACell^.Col + dc2 + else c2 := ACell^.SharedFormulaBase^.Col + dc2; + rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem); end; INT_EXCEL_TOKEN_TMISSARG: rpnItem := RPNMissingArg(rpnItem); @@ -1594,8 +1659,8 @@ begin end; { Reads a SHAREDFMLA record, i.e. reads cell range coordinates and a rpn - formula. The formula is applied to all cells in the range. The formula stored - only in the top/left cell of the range. } + formula. The formula is applied to all cells in the range. The formula is + stored only in the top/left cell of the range. } procedure TsSpreadBIFFReader.ReadSharedFormula(AStream: TStream); var r1, r2, c1, c2: Cardinal;