FPSpreadsheet

From Lazarus wiki
Revision as of 10:16, 3 August 2011 by Garydale (talk | contribs) (Opening an existing spreadsheet: simplified code to use fpspreadsheet consts and also ExtractFileExt)

Deutsch (de) English (en) español (es) français (fr) polski (pl) русский (ru)

The fpSpreadsheet library offers a convenient way to generate and read spreadsheet documents in various formats. The library is written in a very flexible manner, capable of being extended to support any number of formats easily.

API Documentation

API Reference

In CHM format here:

http://lazarus-ccr.svn.sourceforge.net/viewvc/lazarus-ccr/components/fpspreadsheet/fpspreadsheet.chm

Formulas

Two kinds of formulas are supported by FPSpreadsheet: string formulas and RPN formulas. String formulas are written in strings just like in the office application, for example "=ROUND(A1+B1)", while RPN formulas are written in Reverse Polish Notation (RPN), for example: A1, B1, Add, 0, ROUND

RPN Formulas

Using simple constant numbers

<delphi> var

 MyRPNFormula: TsRPNFormula;

begin

 // Write the formula =4+5
 MyWorksheet.WriteUTF8Text(3, 0, '=4+5'); // A4
 //
 SetLength(MyRPNFormula, 3);
 MyRPNFormula[0].ElementKind := fekNum;
 MyRPNFormula[0].DoubleValue := 4.0;
 MyRPNFormula[1].ElementKind := fekNum;
 MyRPNFormula[1].DoubleValue := 5.0;
 MyRPNFormula[2].ElementKind := fekAdd;
 MyWorksheet.WriteRPNFormula(3, 2, MyRPNFormula);

end; </delphi>

Using ranges of cells and the unary SUM operator

<delphi>

 // =Sum(E2:e5)
 MyWorksheet.WriteUTF8Text(1, 0, '=Sum(E2:e5)'); // A2
 //
 SetLength(MyRPNFormula, 2);
 MyRPNFormula[0].ElementKind := fekCellRange;
 MyRPNFormula[0].Row := 1;
 MyRPNFormula[0].Row2 := 4;
 MyRPNFormula[0].Col := 4;
 MyRPNFormula[0].Col2 := 4;
 MyRPNFormula[1].ElementKind := fekOpSUM;
 MyWorksheet.WriteRPNFormula(1, 2, MyRPNFormula);   // C2

</delphi>

Using built-in routines

<delphi>

 // Write the formula E1 = ABS(A1)
 SetLength(MyRPNFormula, 2);
 MyRPNFormula[0].ElementKind := fekCell;
 MyRPNFormula[0].Col := 0;
 MyRPNFormula[0].Row := 0;
 MyRPNFormula[1].ElementKind := fekABS;
 MyWorksheet.WriteRPNFormula(0, 4, MyRPNFormula);
 // Write the formula F1 = ROUND(A1, 0)
 SetLength(MyRPNFormula, 3);
 MyRPNFormula[0].ElementKind := fekCell;
 MyRPNFormula[0].Col := 0;
 MyRPNFormula[0].Row := 0;
 MyRPNFormula[1].ElementKind := fekNum;
 MyRPNFormula[1].DoubleValue := 0.0;
 MyRPNFormula[2].ElementKind := fekROUND;
 MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula);

</delphi>

Built-in routines

ABS - Returns the absolute value of a given number. Takes 1 argument. Example: ABS(-1) = 1

ROUND - Rounds a number to a given number of decimal places. Takes 2 arguments, first the number to be rounded and then the number of decimal places to be rounded to. This second argument is zero by default and may be negative.

Packages

FPSpreadsheet comes with the following packages to facilitate it's usage with:

laz_fpspreadsheet.lpk

Adds all necessary files to use fpspreadsheet in your Lazarus projects. After adding this package as a requirement to your project you can add "fpspreadsheet" to the uses clause of the units which use it.

laz_fpspreadsheet_visual.lpk

Adds a set of visual components which expands the capabilities of FPSpreadsheet to also include editing and displaying data on screen inside LCL applications.

The following LCL components are installed with this package:

Examples

To create a project which uses the fpspreadsheet library, add the fpspreadsheet_pkg package to it's Lazarus project, or add the base directory of fpspreadsheet to you compiler options if using another IDE.

Excel 5 example

<delphi> { excel5demo.dpr

Demonstrates how to write an Excel 5.x file using the fpspreadsheet library

You can change the output format by changing the OUTPUT_FORMAT constant

AUTHORS: Felipe Monteiro de Carvalho } program excel5demo;

{$mode delphi}{$H+}

uses

 Classes, SysUtils, fpspreadsheet, fpsallformats, fpspreadsheet_pkg;

const OUTPUT_FORMAT = sfExcel5;

var

 MyWorkbook: TsWorkbook;
 MyWorksheet: TsWorksheet;
 MyFormula: TsRPNFormula;
 MyDir: string;

begin

 // Initialization
 MyDir := ExtractFilePath(ParamStr(0));
 // Create the spreadsheet
 MyWorkbook := TsWorkbook.Create;
 MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet');
 // Write some number cells
 MyWorksheet.WriteNumber(0, 0, 1.0);
 MyWorksheet.WriteNumber(0, 1, 2.0);
 MyWorksheet.WriteNumber(0, 2, 3.0);
 MyWorksheet.WriteNumber(0, 3, 4.0);
 // Write the formula E1 = A1 + B1
 // or, in RPN: A1, B1, +
 SetLength(MyFormula, 3);
 MyFormula[0].ElementKind:=fekCell; {A1}
 MyFormula[0].Col := 0;
 MyFormula[0].Row := 0;
 MyFormula[1].ElementKind:=fekCell; {B1}
 MyFormula[1].Col := 1;
 MyFormula[1].Row := 0;
 MyFormula[2].ElementKind:=fekAdd;;  {+}
 MyWorksheet.WriteRPNFormula(0, 4, MyFormula);
 // Creates a new worksheet
 MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet 2');
 // Write some string cells
 MyWorksheet.WriteUTF8Text(0, 0, 'First');
 MyWorksheet.WriteUTF8Text(0, 1, 'Second');
 MyWorksheet.WriteUTF8Text(0, 2, 'Third');
 MyWorksheet.WriteUTF8Text(0, 3, 'Fourth');
 // Save the spreadsheet to a file
 MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, OUTPUT_FORMAT);
 MyWorkbook.Free;

end. </delphi>

Opening an existing spreadsheet

This is some code I developed to open any file usable by fpspreadsheet. It uses the filename suffix to determine the spreadsheet type. Usually the FileToUpdate would be set via a file open dialog, so I don't check it. If the file opens OK then MyWorkBook contains the spreadsheet. Otherwise it is empty.

This is similar to the overloaded ReadFromFile method for TsWorkBook except: - this falls back to earlier Excel spreadsheet types if the .xls spreadsheet doesn't open as sfExcel8, - this intercepts ReadFromFile failures and tries to handle them, - it handles the .csv file type which is listed as a supported type but is not used in the ReadFromFile method. Note also that this is not a TsWorkBook method. It creates and loads a workbook from a successfully opened file. Otherwise it exits with no workbook created.


<delphi> procedure OpenSpreadsheet(const FileToUpdate: String; var MyWorkBook: TsWorkBook); var SheetType: TsSpreadsheetFormat;

   valid: Boolean;
 procedure getFileType(const FileToUpdate: String; var SheetType: TsSpreadsheetFormat; var valid: Boolean);
   var suffix: String;
   begin
       valid := True;
       suffix := LowerCase(ExtractFileExt(FileToUpdate));
       if suffix = STR_EXCEL_EXTENSION then
          SheetType := sfExcel8
       else if suffix = STR_OPENDOCUMENT_CALC_EXTENSION  then
          SheetType := sfOpenDocument
       else if suffix = STR_OOXML_EXCEL_EXTENSION then
          SheetType := sfOOXML
       else if suffix = '.csv' then
          SheetType := sfCSV
       else
          valid := False;
   end;
 begin
     getFileType(FileToUpdate, SheetType, valid);
     if valid then begin
        try
           MyWorkbook := TsWorkbook.Create;
           MyWorkbook.ReadFromFile(FileToUpdate, SheetType);
        except
           MyWorkBook.Destroy;
           valid := False;
           if SheetType = sfExcel8 then begin
              repeat
                 try
                    SheetType := Pred(SheetType);
                    MyWorkbook := TsWorkbook.Create;
                    MyWorkbook.ReadFromFile(FileToUpdate, SheetType);
                    valid := True;
                 except
                    MyWorkBook.Destroy;
                 end;
               until valid or (SheetType = sfExcel2);
           end;
        end;
     end;

end; </delphi>

Iterating through all Worksheets

<delphi> var

 MyWorkbook: TsWorkbook;
 MyWorksheet: TsWorksheet;
 i: Integer;

begin

 // Here load MyWorkbook from a file or build it

 for i := 0 to MyWorkbook.GetWorksheetCount() - 1 do
 begin
   MyWorksheet := MyWorkbook.GetWorksheetByIndex(i);
   // Do something with MyWorksheet
 end;

</delphi>

Converting a database to a spreadsheet

Note that this is only meta-code, not tested.

<delphi> program db5xls;

{$mode delphi}{$H+}

uses

 Classes, SysUtils, 
 // add database units
 fpspreadsheet, fpsallformats, fpspreadsheet_pkg;

const OUTPUT_FORMAT = sfExcel5;

var

 MyWorkbook: TsWorkbook;
 MyWorksheet: TsWorksheet;
 MyDatabase: TSdfDataset;
 MyDir: string;
 i, j: Integer;

begin

 // Initialization
 MyDir := ExtractFilePath(ParamStr(0));

 // Open the database
 MyDatabase := TSdfDataset.Create;
 MyDatabase.Filename := 'test.dat';
 // Add table description here
 MyDatabase.Active := True;
 // Create the spreadsheet
 MyWorkbook := TsWorkbook.Create;
 MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet');

 // Write the field names
 for i := 0 to MyDatabase.Fields.Count - 1 do
   MyWorksheet.WriteUTF8Text(0, i, MyDatabase.Field[i].FieldName);
 // Write all cells to the worksheet
 MyDatabase.First;
 j := 0;
 while not MyDatabase.EOF do
 begin
   for i := 0 to MyDatabase.Fields.Count - 1 do
     MyWorksheet.WriteUTF8Text(j + 1, i, MyDatabase.Field[i].AsString);
   MyDatabase.Next;
   Inc(j);
 end;
 // Close the database
 MyDatabase.Active := False;
 MyDatabase.Free;
 // Save the spreadsheet to a file
 MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, OUTPUT_FORMAT);
 MyWorkbook.Free;

end. </delphi>

Converting between two spreadsheet formats

Note that this is only meta-code, not tested.

<delphi> program ods2xls;

{$mode delphi}{$H+}

uses

 Classes, SysUtils, 
 fpspreadsheet, fpsallformats, fpspreadsheet_pkg;

const

 INPUT_FORMAT = sfOpenDocument;
 OUTPUT_FORMAT = sfExcel8;

var

 MyWorkbook: TsWorkbook;
 MyDir: string;

begin

 // Initialization
 MyDir := ExtractFilePath(ParamStr(0));

 // Convert the spreadsheet
 MyWorkbook := TsWorkbook.Create;
 try
   MyWorkbook.ReadFromFile(MyDir + 'test.ods', INPUT_FORMAT);
   MyWorkbook.WriteToFile(MyDir + 'test.xls', OUTPUT_FORMAT);
 finally
   MyWorkbook.Free;
 end;

end. </delphi>

Using formatting options

The formatting is set independently from the text, as in the example bellow:

<delphi>

 // Write a bold text
 MyWorksheet.WriteUTF8Text(0, 0, 'First');
 MyWorksheet.WriteUsedFormatting(0, 0, [uffBold]);

</delphi>

Setting the text orientation

There are three possible text rotations: trHorizontal, rt90DegreeClockwiseRotation and rt90DegreeCounterClockwiseRotation

They are relative to the standard text position.

<delphi>

 // Write a bold text
 MyWorksheet.WriteUTF8Text(0, 0, 'First');
 MyCell := MyWorksheet.GetCell(0, 0);
 MyCell^.TextRotation := rt90DegreeClockwiseRotation;
 MyCell^.UsedFormattingFields := [uffTextRotation];

</delphi>

Setting a Border

<delphi>

 // Write a text with borders and background color
 MyWorksheet.WriteUTF8Text(5, 9, '[N,W,E,S]');// J6
 MyCell := MyWorksheet.GetCell(5, 9);
 MyCell^.Border := [cbNorth, cbWest, cbEast, cbSouth];
 MyCell^.UsedFormattingFields := [uffBorder, uffBold, uffBackgroundColor];

</delphi>

Setting a Background Color

One can set one of 16 colors from a pallete to set to the background. The possible values are:

<delphi>

 {@@ Colors in FPSpreadsheet as given by a pallete to be compatible with Excel }
 TsColor = (
   scBlack,    // 000000H
   scWhite,    // FFFFFFH
   scRed,      // FF0000H
   scGREEN,    // 00FF00H
   scBLUE,     // 0000FFH
   scYELLOW,   // FFFF00H
   scMAGENTA,  // FF00FFH
   scCYAN,     // 00FFFFH
   scDarkRed,  // 800000H
   scDarkGreen,// 008000H
   scDarkBlue, // 000080H
   scOLIVE,    // 808000H
   scPURPLE,   // 800080H
   scTEAL,     // 008080H
   scSilver,   // C0C0C0H
   scGrey      // 808080H
   //
   scGrey10pct,// E6E6E6H
   scGrey20pct // CCCCCCH
 );

</delphi>

And an example:

<delphi>

 // Write a text with background color
 MyWorksheet.WriteUTF8Text(0, 0, 'Test');
 MyCell := MyWorksheet.GetCell(0, 0);
 MyCell^.BackgroundColor := scSilver;
 MyCell^.UsedFormattingFields := [uffBackgroundColor];

</delphi>

Combining Multiple Formattings

<delphi>

 // Write a text with borders and background color
 MyWorksheet.WriteUTF8Text(5, 9, '[N,W,E,S]');
 MyCell := MyWorksheet.GetCell(5, 9);
 MyCell^.Border := [cbNorth, cbWest, cbEast, cbSouth];
 MyCell^.UsedFormattingFields := [uffBorder, uffBold, uffBackgroundColor];

</delphi>

Grid and Charting Controls with FPSpreadsheet

See TsWorksheetChartSource

Download

Subversion

You can download FPSpreadsheet using the subversion software and the following command line:

svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/components/fpspreadsheet fpspreadsheet

Support and Bug Reporting

The recommended place to discuss FPSpreadsheet and obtain support is asking in the Lazarus Forum: http://www.lazarus.freepascal.org/index.php/board,42.0.html

Bug reports should be sent to the Free Pascal Bug Tracker in the "Lazarus-CCR" section: http://bugs.freepascal.org/

Current Progress

Progress by supported format:

Format Supports multiple sheets? Supports Unicode? Reader Progress Writer Progress Text Number String Formula RPN Formula
Excel 2.x No No** Working Working Working Working Not implemented Working
Excel 3.0 No No** Not implemented Not implemented Not implemented Not implemented Not implemented Not implemented
Excel 4.0 No No** Not implemented Not implemented Not implemented Not implemented Not implemented Not implemented
Excel 5.0 (Excel 5.0 and 95) Yes No** Working* Working Working Working Not implemented Working
Excel 8.0 (Excel 97, 2000, XP and 2003) Yes Yes Working* Working Working Working Not implemented Working
Microsoft OOXML Yes Yes Not implemented Not implemented Not implemented Not implemented Not implemented Not implemented
OpenDocument Yes Yes Working Working Working Working Not implemented Not implemented

(*) Some cell could be returned blank due missing or non ready implemented number and text formats.
(**) In formats which don't support Unicode the data is stored by default as ISO 8859-1 (Latin 1). You can change the encoding in TsWorkbook.Encoding. Note that FPSpreadsheet offers UTF-8 read and write routines, but the data might be converted to ISO when reading or writing to the disk. Be careful that characters which don't fit selected encoding will be lost in those operations. The remarks here are only valid for formats which don't support Unicode.

Progress of the formatting options

Some formatting options were added, but they aren't yet implemented for all formats:

Format Text rotation Bold Border Background Color
Excel 2.x Not implemented Not implemented Not implemented Not implemented
Excel 3.0 Not implemented Not implemented Not implemented Not implemented
Excel 4.0 Not implemented Not implemented Not implemented Not implemented
Excel 5.0 (Excel 5.0 and 95) - - Not implemented Not implemented
Excel 8.0 (Excel 97, 2000, XP and 2003) Working Working Working Working
Microsoft OOXML Not implemented Not implemented Not implemented Not implemented
OpenDocument Working Working Working Working

Changelog

Jan 2009

  • Implemented a cross-platform support for OLE file. Now Excel 5.0 files can be created in any operating system.
  • Adds read support for Excel 2.1

Feb 2008

  • Initial commit to lazarus-ccr with write support for Excel 2.1, Excel 5.0 (Windows only) and experimental OOXML and OpenDocument

License

LGPL with static linking exception. This is the same license as is used in the Lazarus Component Library.

See also

External Links