From a0a19eb380bcc1c2a835d1a76f05b2f5567a1cfe Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 8 Aug 2014 19:00:27 +0000 Subject: [PATCH] fpspreadsheet: Write OOXML string formulas. Write biff2 dimension record. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3456 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpsfunc.pas | 2 - components/fpspreadsheet/fpspreadsheet.pas | 33 ++++++++++++- .../fpspreadsheet/tests/spreadtestgui.lpi | 4 +- components/fpspreadsheet/xlsbiff2.pas | 36 ++++++++++++++ components/fpspreadsheet/xlsbiff5.pas | 29 +---------- components/fpspreadsheet/xlsbiff8.pas | 24 --------- components/fpspreadsheet/xlsxooxml.pas | 49 ++++++++++++++----- 7 files changed, 106 insertions(+), 71 deletions(-) diff --git a/components/fpspreadsheet/fpsfunc.pas b/components/fpspreadsheet/fpsfunc.pas index cb8d0112b..f3e3cbf0b 100644 --- a/components/fpspreadsheet/fpsfunc.pas +++ b/components/fpspreadsheet/fpsfunc.pas @@ -1372,7 +1372,6 @@ function fpsDATEDIF(Args: TsArgumentStack; NumArgs: Integer): TsArgument; = YD - The difference between the days (years and dates are ignored). } var interval: String; - data: TsArgStringArray; start_date, end_date: TDate; res1, res2, res3: TsArgument; begin @@ -1599,7 +1598,6 @@ function fpsCOUNTBLANK(Args: TsArgumentStack; NumArgs: Integer): TsArgument; var arg: TsArgument; r, c, n: Cardinal; - cell: PCell; begin arg := Args.Pop; case arg.ArgumentType of diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 327f3af4e..b37145a38 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -584,6 +584,8 @@ type function WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula): PCell; overload; procedure WriteFormula(ACell: PCell; AFormula: TsFormula); overload; + function WriteFormula(ARow, ACol: Cardinal; AFormula: String): PCell; overload; + procedure WriteFormula(ACell: PCell; AFormula: String); overload; procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double); overload; procedure WriteNumber(ACell: PCell; ANumber: Double); overload; @@ -1700,7 +1702,6 @@ end; procedure TsWorksheet.CalcRPNFormula(ACell: PCell); var i: Integer; - formula: TsRPNFormula; args: TsArgumentStack; func: TsFormulaFunc; val: TsArgument; @@ -2342,7 +2343,6 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring; var fs: TFormatSettings; - left, right: String; begin fs := FWorkbook.FormatSettings; if IsNan(Value) then @@ -3456,6 +3456,35 @@ begin ChangedCell(ACell^.Row, ACell^.Col); end; +{@@ + Writes a formula to a given cell + + @param ARow The row of the cell + @param ACol The column of the cell + @param AFormula The formula string to be written + @return Pointer to the cell +} +function TsWorksheet.WriteFormula(ARow, ACol: Cardinal; AFormula: string): PCell; +begin + Result := GetCell(ARow, ACol); + WriteFormula(Result, AFormula); +end; + +{@@ + Writes a formula to a given cell + + @param ACell Pointer to the cell + @param AFormula Formula string to be written +} +procedure TsWorksheet.WriteFormula(ACell: PCell; AFormula: String); +begin + if ACell = nil then + exit; + ACell^.ContentType := cctFormula; + ACell^.FormulaValue.FormulaStr := AFormula; + ChangedCell(ACell^.Row, ACell^.Col); +end; + {@@ Adds a number format to the formatting of a cell diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index e4dc9f4dd..15be00ba3 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -52,7 +52,6 @@ - @@ -66,16 +65,15 @@ - - + diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index aa51f57b8..725e0b7f0 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -93,6 +93,7 @@ type procedure WriteCellFormatting(AStream: TStream; ACell: PCell; XFIndex: Word); procedure WriteColWidth(AStream: TStream; ACol: PCol); procedure WriteColWidths(AStream: TStream); + procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteEOF(AStream: TStream); procedure WriteFont(AStream: TStream; AFontIndex: Integer); procedure WriteFonts(AStream: TStream); @@ -148,6 +149,7 @@ uses const { Excel record IDs } + INT_EXCEL_ID_DIMENSIONS = $0000; INT_EXCEL_ID_BLANK = $0001; INT_EXCEL_ID_INTEGER = $0002; INT_EXCEL_ID_NUMBER = $0003; @@ -169,6 +171,15 @@ const INT_EXCEL_MACRO_SHEET = $0040; type + TBIFF2DimensionsRecord = packed record + RecordID: Word; + RecordSize: Word; + FirstRow: Word; + LastRowPlus1: Word; + FirstCol: Word; + LastColPlus1: Word; + end; + TBIFF2LabelRecord = packed record RecordID: Word; RecordSize: Word; @@ -1112,6 +1123,30 @@ begin end; end; +{ + Writes an Excel 2 DIMENSIONS record +} +procedure TsSpreadBIFF2Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); +var + firstRow, lastRow, firstCol, lastCol: Cardinal; + rec: TBIFF2DimensionsRecord; +begin + { Determine sheet size } + GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol); + + { Populate BIFF record } + rec.RecordID := WordToLE(INT_EXCEL_ID_DIMENSIONS); + rec.RecordSize := WordToLE(8); + rec.FirstRow := WordToLE(firstRow); + rec.LastRowPlus1 := WordToLE(Min(lastRow+1, $FFFF)); // avoid WORD overflow + rec.FirstCol := WordToLE(firstCol); + rec.LastColPlus1 := WordToLE(lastCol+1); + + { Write BIFF record to stream } + AStream.WriteBuffer(rec, SizeOf(rec)); +end; + + { Writes an Excel 2 IXFE record This record contains the "real" XF index if it is > 62. @@ -1143,6 +1178,7 @@ begin WriteFormats(AStream); WriteXFRecords(AStream); WriteColWidths(AStream); + WriteDimensions(AStream, sheet); WriteRows(AStream, sheet); if (boVirtualMode in Workbook.Options) then diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index 9ff85c300..ba1ee6d07 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -219,7 +219,7 @@ var implementation uses - fpsStreams; + Math, fpsStreams; const { Excel record IDs } @@ -563,7 +563,6 @@ procedure TsSpreadBIFF5Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWo var rec: TBIFF5DimensionsRecord; firstCol, lastCol, firstRow, lastRow: Cardinal; - lLastRow, lLastCol: Word; begin { Determine sheet size } GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol); @@ -572,37 +571,13 @@ begin rec.RecordID := WordToLE(INT_EXCEL_ID_DIMENSIONS); rec.RecordSize := WordToLE(10); rec.FirstRow := WordToLE(firstRow); - rec.LastRowPlus1 := WordToLE(lastRow+1); + rec.LastRowPlus1 := WordToLE(Min(lastRow+1, $FFFF)); // avoid word overflow rec.FirstCol := WordToLe(firstCol); rec.LastColPlus1 := WordToLE(lastCol+1); rec.NotUsed := 0; { Write BIFF record } AStream.WriteBuffer(rec, SizeOf(rec)); - - (* - - { BIFF Record header } - AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS)); - AStream.WriteWord(WordToLE(10)); - - { Index to first used row } - AStream.WriteWord(0); - - { Index to last used row, increased by 1 } - lLastRow := Word(GetLastRowIndex(AWorksheet)+1); - AStream.WriteWord(WordToLE(lLastRow)); // Old dummy value: 33 - - { Index to first used column } - AStream.WriteWord(0); - - { Index to last used column, increased by 1 } - lLastCol := Word(GetLastColIndex(AWorksheet)+1); - AStream.WriteWord(WordToLE(lLastCol)); // Old dummy value: 10 - - { Not used } - AStream.WriteWord(0); - *) end; {******************************************************************* diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 2a31c3f03..0a80dcfdc 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -606,30 +606,6 @@ begin { Write BIFF record to stream } AStream.WriteBuffer(rec, SizeOf(rec)); - - (* - { BIFF Record header } - AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS)); - AStream.WriteWord(WordToLE(14)); - - { Determine sheet size } - GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol); - - { Index to first used row } - AStream.WriteDWord(DWordToLE(firstRow)); - - { Index to last used row, increased by 1 } - AStream.WriteDWord(DWordToLE(lastRow+1)); - - { Index to first used column } - AStream.WriteWord(WordToLE(firstCol)); - - { Index to last used column, increased by 1 } - AStream.WriteWord(WordToLE(lastCol+1)); - - { Not used } - AStream.WriteWord(WordToLE(0)); - *) end; {******************************************************************* diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas index 4694ca6ae..f28c8efa3 100755 --- a/components/fpspreadsheet/xlsxooxml.pas +++ b/components/fpspreadsheet/xlsxooxml.pas @@ -114,6 +114,7 @@ type function GetStyleIndex(ACell: PCell): Cardinal; procedure ListAllBorders; procedure ListAllFills; + function PrepareFormula(const AFormula: TsFormula): String; procedure ResetStreams; procedure WriteBorderList(AStream: TStream); procedure WriteCols(AStream: TStream; AWorksheet: TsWorksheet); @@ -143,10 +144,16 @@ type protected { Record writing methods } //todo: add WriteDate - procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override; - procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; - procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; - procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override; + procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; + ACell: PCell); override; + procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; + const AFormula: TsFormula; ACell: PCell); override; + procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; + const AValue: string; ACell: PCell); override; + procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; + const AValue: double; ACell: PCell); override; + procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; + const AValue: TDateTime; ACell: PCell); override; public constructor Create(AWorkbook: TsWorkbook); override; @@ -1907,15 +1914,6 @@ begin end else begin // The cells need to be written in order, row by row, cell by cell - (* - c1 := AWorksheet.GetFirstColIndex; - c2 := AWorksheet.GetLastColIndex; - if (c1 = $FFFFFFFF) and (c2 = 0) then c1 := 0; // avoid arithmetic overflow in case of empty worksheet - r1 := AWorksheet.GetFirstRowIndex; - r2 := AWorksheet.GetlastRowIndex; - if (r1 = $FFFFFFFF) and (r2 = 0) then r1 := 0; // avoid arithmetic overflow in case of empty worksheet -// for r := 0 to AWorksheet.GetLastRowIndex do begin - *) for r := r1 to r2 do begin // If the row has a custom height add this value to the specification row := AWorksheet.FindRow(r); @@ -2364,6 +2362,13 @@ begin SetLength(FSSheets, 0); end; +{ Prepares a string formula for writing } +function TsSpreadOOXMLWriter.PrepareFormula(const AFormula: TsFormula): String; +begin + Result := AFormula.FormulaStr; + if (Result <> '') and (Result[1] = '=') then Delete(Result, 1, 1); +end; + { Is called before zipping the individual file parts. Rewinds the streams. } procedure TsSpreadOOXMLWriter.ResetStreams; var @@ -2486,6 +2491,24 @@ begin ''); end; +{ Writes a string formula to the given cell. } +procedure TsSpreadOOXMLWriter.WriteFormula(AStream: TStream; + const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); +var + cellPosText: String; + lStyleIndex: Integer; +begin + cellPosText := TsWorksheet.CellPosToText(ARow, ACol); + lStyleIndex := GetStyleIndex(ACell); + + AppendToStream(AStream, Format( + '' + + '%s' + + '', [ + CellPosText, lStyleIndex, + PrepareFormula(AFormula) + ])); +end; {******************************************************************* * TsSpreadOOXMLWriter.WriteLabel ()