FPSpreadsheet: Adds some code for formula handling

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1659 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat 2011-05-30 09:22:08 +00:00
parent e23fbc9f1d
commit f62592925f
5 changed files with 426 additions and 2 deletions

View File

@ -0,0 +1,215 @@
<?xml version="1.0"?>
<CONFIG>
<ProjectOptions>
<Version Value="9"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
</Flags>
<MainUnit Value="0"/>
<Title Value="test_write_formula"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<Icon Value="0"/>
<ActiveWindowIndexAtStart Value="0"/>
</General>
<i18n>
<EnableI18N LFM="False"/>
</i18n>
<VersionInfo>
<StringTable ProductVersion=""/>
</VersionInfo>
<BuildModes Count="1" Active="Default">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
<ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
<LaunchingApplication PathPlusParams="/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="laz_fpspreadsheet"/>
</Item1>
</RequiredPackages>
<Units Count="4">
<Unit0>
<Filename Value="test_write_formula.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="test_write_formula"/>
<IsVisibleTab Value="True"/>
<EditorIndex Value="0"/>
<WindowIndex Value="0"/>
<TopLine Value="23"/>
<CursorPos X="42" Y="45"/>
<UsageCount Value="20"/>
<Loaded Value="True"/>
</Unit0>
<Unit1>
<Filename Value="../../fpspreadsheet.pas"/>
<UnitName Value="fpspreadsheet"/>
<EditorIndex Value="3"/>
<WindowIndex Value="0"/>
<TopLine Value="38"/>
<CursorPos X="13" Y="66"/>
<UsageCount Value="10"/>
<Loaded Value="True"/>
</Unit1>
<Unit2>
<Filename Value="../../xlsbiff8.pas"/>
<UnitName Value="xlsbiff8"/>
<EditorIndex Value="1"/>
<WindowIndex Value="0"/>
<TopLine Value="840"/>
<CursorPos X="10" Y="862"/>
<UsageCount Value="10"/>
<Loaded Value="True"/>
</Unit2>
<Unit3>
<Filename Value="../../xlscommon.pas"/>
<UnitName Value="xlscommon"/>
<EditorIndex Value="2"/>
<WindowIndex Value="0"/>
<TopLine Value="125"/>
<CursorPos X="1" Y="150"/>
<UsageCount Value="10"/>
<Loaded Value="True"/>
</Unit3>
</Units>
<JumpHistory Count="25" HistoryIndex="24">
<Position1>
<Filename Value="test_write_formula.pas"/>
<Caret Line="3" Column="1" TopLine="1"/>
</Position1>
<Position2>
<Filename Value="test_write_formula.pas"/>
<Caret Line="179" Column="1" TopLine="136"/>
</Position2>
<Position3>
<Filename Value="test_write_formula.pas"/>
<Caret Line="29" Column="17" TopLine="10"/>
</Position3>
<Position4>
<Filename Value="test_write_formula.pas"/>
<Caret Line="27" Column="24" TopLine="1"/>
</Position4>
<Position5>
<Filename Value="../../fpspreadsheet.pas"/>
<Caret Line="205" Column="64" TopLine="181"/>
</Position5>
<Position6>
<Filename Value="test_write_formula.pas"/>
<Caret Line="35" Column="22" TopLine="3"/>
</Position6>
<Position7>
<Filename Value="test_write_formula.pas"/>
<Caret Line="13" Column="42" TopLine="1"/>
</Position7>
<Position8>
<Filename Value="../../xlsbiff8.pas"/>
<Caret Line="524" Column="14" TopLine="493"/>
</Position8>
<Position9>
<Filename Value="../../fpspreadsheet.pas"/>
<Caret Line="1147" Column="27" TopLine="1123"/>
</Position9>
<Position10>
<Filename Value="../../xlsbiff8.pas"/>
<Caret Line="124" Column="25" TopLine="94"/>
</Position10>
<Position11>
<Filename Value="../../fpspreadsheet.pas"/>
<Caret Line="1072" Column="74" TopLine="1067"/>
</Position11>
<Position12>
<Filename Value="../../fpspreadsheet.pas"/>
<Caret Line="206" Column="70" TopLine="175"/>
</Position12>
<Position13>
<Filename Value="../../xlsbiff8.pas"/>
<Caret Line="850" Column="14" TopLine="831"/>
</Position13>
<Position14>
<Filename Value="../../xlsbiff8.pas"/>
<Caret Line="104" Column="35" TopLine="102"/>
</Position14>
<Position15>
<Filename Value="../../xlscommon.pas"/>
<Caret Line="81" Column="76" TopLine="49"/>
</Position15>
<Position16>
<Filename Value="../../xlscommon.pas"/>
<Caret Line="40" Column="1" TopLine="17"/>
</Position16>
<Position17>
<Filename Value="../../xlscommon.pas"/>
<Caret Line="145" Column="41" TopLine="129"/>
</Position17>
<Position18>
<Filename Value="../../xlscommon.pas"/>
<Caret Line="91" Column="40" TopLine="69"/>
</Position18>
<Position19>
<Filename Value="../../xlscommon.pas"/>
<Caret Line="149" Column="3" TopLine="128"/>
</Position19>
<Position20>
<Filename Value="../../xlsbiff8.pas"/>
<Caret Line="104" Column="35" TopLine="102"/>
</Position20>
<Position21>
<Filename Value="../../xlsbiff8.pas"/>
<Caret Line="857" Column="72" TopLine="835"/>
</Position21>
<Position22>
<Filename Value="test_write_formula.pas"/>
<Caret Line="52" Column="1" TopLine="18"/>
</Position22>
<Position23>
<Filename Value="test_write_formula.pas"/>
<Caret Line="42" Column="27" TopLine="20"/>
</Position23>
<Position24>
<Filename Value="test_write_formula.pas"/>
<Caret Line="45" Column="15" TopLine="23"/>
</Position24>
<Position25>
<Filename Value="test_write_formula.pas"/>
<Caret Line="46" Column="38" TopLine="23"/>
</Position25>
</JumpHistory>
</ProjectOptions>
<CompilerOptions>
<Version Value="10"/>
<Target>
<Filename Value="test_write_formula"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,84 @@
{
test_write_formula.pas
Demonstrates how to write an formula using the fpspreadsheet library
AUTHORS: Felipe Monteiro de Carvalho
}
program test_write_formula;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpspreadsheet, xlsbiff8, fpsopendocument,
laz_fpspreadsheet, fpsconvencoding;
var
MyWorkbook: TsWorkbook;
MyWorksheet: TsWorksheet;
MyDir: string;
MyCell: PCell;
procedure WriteFirstWorksheet();
var
MyFormula: TsFormula;
MyRPNFormula: TsRPNFormula;
begin
MyWorksheet := MyWorkbook.AddWorksheet('Worksheet1');
// Write some cells
MyWorksheet.WriteUTF8Text(1, 0, 'Text Formulas');// A2
MyWorksheet.WriteUTF8Text(1, 1, '=Sum(D2:d5) Text Formula'); // B2
MyFormula.FormulaStr := '=Sum(D2:d5)';
MyFormula.DoubleValue := 0.0;
MyWorksheet.WriteFormula(1, 2, MyFormula); // C2
MyWorksheet.WriteUTF8Text(1, 1, '=Sum(D2:d5) RPN'); // B3
MyFormula.FormulaStr := '=Sum(D2:d5)';
MyFormula.DoubleValue := 0.0;
MyWorksheet.WriteFormula(1, 2, MyFormula); // C3
SetLength(MyRPNFormula, 2);
MyRPNFormula[0].ElementKind := fekOpSUM;
MyRPNFormula[1].ElementKind := fekCellRange;
MyRPNFormula[1].Row := 1;
MyRPNFormula[1].Row := 4;
MyRPNFormula[1].Col := 3;
MyRPNFormula[1].Col := 3;
MyWorksheet.WriteRPNFormula(1, 2, MyRPNFormula); // C2
end;
procedure WriteSecondWorksheet();
begin
{ MyWorksheet := MyWorkbook.AddWorksheet('Worksheet2');
// Write some cells
// Line 1
MyWorksheet.WriteUTF8Text(1, 1, 'Relatório');
MyCell := MyWorksheet.GetCell(1, 1);
MyCell^.Border := [cbNorth, cbWest, cbSouth];
MyCell^.BackgroundColor := scGrey20pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor, uffBold];}
end;
begin
MyDir := ExtractFilePath(ParamStr(0));
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
WriteFirstWorksheet();
WriteSecondWorksheet();
// Save the spreadsheet to a file
MyWorkbook.WriteToFile(MyDir + 'test_formula.xls', sfExcel8, False);
// MyWorkbook.WriteToFile(MyDir + 'test_formula.odt', sfOpenDocument, False);
MyWorkbook.Free;
end.

View File

@ -57,7 +57,7 @@ type
TFEKind = (
{ Basic operands }
fekCell, fekNum,
fekCell, fekCellRange, fekNum,
{ Basic operations }
fekAdd, fekSub, fekDiv, fekMul,
{ Build-in Functions}
@ -1161,6 +1161,14 @@ begin
IterateThroughCells(AStream, ACells, WriteCellCallback);
end;
{@@
A generic method to iterate through all cells in a worksheet and call a callback
routine for each cell.
@param AStream The output stream, passed to the callback routine.
@param ACells List of cells to be iterated
@param ACallback The callback routine
}
procedure TsCustomSpreadWriter.IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback);
var
AVLNode: TAVLTreeNode;

View File

@ -121,6 +121,7 @@ type
procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TFPCustomFont);
procedure WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TsFormula); override;
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TsRPNFormula); override;
procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string; ACell: PCell); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
@ -808,6 +809,88 @@ begin
AStream.position := FinalPos;*)
end;
procedure TsSpreadBIFF8Writer.WriteRPNFormula(AStream: TStream; const ARow,
ACol: Word; const AFormula: TsRPNFormula);
var
FormulaResult: double;
i: Integer;
RPNLength: Word;
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
TokenID: Byte;
begin
RPNLength := 0;
FormulaResult := 0.0;
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
RecordSizePos := AStream.Position;
AStream.WriteWord(WordToLE(22 + RPNLength));
{ BIFF Record data }
AStream.WriteWord(WordToLE(ARow));
AStream.WriteWord(WordToLE(ACol));
{ Index to XF Record }
AStream.WriteWord($0000);
{ Result of the formula in IEE 754 floating-point value }
AStream.WriteBuffer(FormulaResult, 8);
{ Options flags }
AStream.WriteWord(WordToLE(MASK_FORMULA_RECALCULATE_ALWAYS));
{ Not used }
AStream.WriteDWord(0);
{ Formula }
{ The size of the token array is written later,
because it's necessary to calculate if first,
and this is done at the same time it is written }
TokenArraySizePos := AStream.Position;
AStream.WriteWord(RPNLength);
{ Formula data (RPN token array) }
for i := 0 to Length(AFormula) - 1 do
begin
{ Token identifier }
TokenID := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind);
AStream.WriteByte(TokenID);
Inc(RPNLength);
{ Additional data }
case TokenID of
{ binary operation tokens }
INT_EXCEL_TOKEN_TADD, INT_EXCEL_TOKEN_TSUB, INT_EXCEL_TOKEN_TMUL,
INT_EXCEL_TOKEN_TDIV, INT_EXCEL_TOKEN_TPOWER: begin end;
INT_EXCEL_TOKEN_TNUM:
begin
AStream.WriteBuffer(AFormula[i].DoubleValue, 8);
Inc(RPNLength, 8);
end;
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA:
begin
AStream.WriteWord(AFormula[i].Row and MASK_EXCEL_ROW);
AStream.WriteByte(AFormula[i].Col);
Inc(RPNLength, 3);
end;
end;
end;
{ Write sizes in the end, after we known them }
FinalPos := AStream.Position;
AStream.position := TokenArraySizePos;
AStream.WriteByte(RPNLength);
AStream.Position := RecordSizePos;
AStream.WriteWord(WordToLE(22 + RPNLength));
AStream.position := FinalPos;
end;
{*******************************************************************
* TsSpreadBIFF8Writer.WriteIndex ()
*

View File

@ -19,7 +19,17 @@ const
INT_EXCEL_TOKEN_TSUB = $04;
INT_EXCEL_TOKEN_TMUL = $05;
INT_EXCEL_TOKEN_TDIV = $06;
INT_EXCEL_TOKEN_TPOWER = $07;
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation
INT_EXCEL_TOKEN_TCONCAT = $08;
INT_EXCEL_TOKEN_TLT = $09; // Less than
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal
INT_EXCEL_TOKEN_TEQ = $0B; // Equal
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal
INT_EXCEL_TOKEN_TGT = $0D; // Greater than
INT_EXCEL_TOKEN_TNE = $0E; // Not equal
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
INT_EXCEL_TOKEN_TRANGE = $11; // Cell range
{ Constant Operand Tokens }
INT_EXCEL_TOKEN_TNUM = $1F;
@ -78,6 +88,7 @@ type
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
function FormulaElementKindToExcelTokenID(AElementKind: TFEKind): Byte;
end;
implementation
@ -131,5 +142,28 @@ begin
Result := FLastCol;
end;
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
AElementKind: TFEKind): Byte;
begin
case AElementKind of
{ Operand Tokens }
fekCell: Result := INT_EXCEL_TOKEN_TREFR;
fekCellRange: Result := INT_EXCEL_TOKEN_TRANGE;
fekNum: Result := INT_EXCEL_TOKEN_TNUM;
{ Basic operations }
fekAdd: Result := INT_EXCEL_TOKEN_TADD;
fekSub: Result := INT_EXCEL_TOKEN_TSUB;
fekDiv: Result := INT_EXCEL_TOKEN_TDIV;
fekMul: Result := INT_EXCEL_TOKEN_TMUL;
{ Build-in Functions}
fekABS: Result := INT_EXCEL_SHEET_FUNC_ABS;
fekROUND: Result := INT_EXCEL_SHEET_FUNC_ROUND;
{ Other operations }
fekOpSUM: Result := 0;
else
Result := 0;
end;
end;
end.