{ fpspreadsheet.pas Writes a spreadsheet document AUTHORS: Felipe Monteiro de Carvalho } unit fpspreadsheet; {$ifdef fpc} {$mode delphi} {$endif} interface uses {$ifdef UNIX}{$ifndef DARWIN}{$ifndef FPS_DONT_USE_CLOCALE} clocale, {$endif}{$endif}{$endif} Classes, SysUtils, fpimage, AVL_Tree, avglvltree, lconvencoding; type {@@ File formats suppored by fpspreadsheet } TsSpreadsheetFormat = (sfExcel2, {sfExcel3, sfExcel4,} sfExcel5, sfExcel8, sfOOXML, sfOpenDocument, sfCSV, sfWikiTable_Pipes, sfWikiTable_WikiMedia); {@@ Record collection limitations of a particular file format } TsSpreadsheetFormatLimitations = record MaxRowCount: Cardinal; MaxColCount: Cardinal; MaxPaletteSize: Cardinal; end; const { Default extensions } STR_EXCEL_EXTENSION = '.xls'; STR_OOXML_EXCEL_EXTENSION = '.xlsx'; STR_OPENDOCUMENT_CALC_EXTENSION = '.ods'; STR_COMMA_SEPARATED_EXTENSION = '.csv'; STR_WIKITABLE_PIPES = '.wikitable_pipes'; STR_WIKITABLE_WIKIMEDIA = '.wikitable_wikimedia'; MAX_COL_COUNT = 65535; type {@@ Possible encodings for a non-unicode encoded text } TsEncoding = ( seLatin1, seLatin2, seCyrillic, seGreek, seTurkish, seHebrew, seArabic ); (* {@@ Describes a formula Supported syntax:
=A1+B1+C1/D2... - Array with simple mathematical operations =SUM(A1:D1) - SUM operation in a interval} TsFormula = record FormulaStr: string; DoubleValue: double; end; *) {@@ Tokens to identify the elements in an expanded formula. See http://www.techonthenet.com/excel/formulas/ for an explanation of meaning and parameters of each formula NOTE: When adding or rearranging items: - make sure that the subtypes TOperandTokens, TBasicOperationTokens and TFuncTokens are complete - make sure to keep the FEProps table in sync - make sure to keep the TokenID table in TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID, unit xlscommon, in sync } TFEKind = ( { Basic operands } fekCell, fekCellRef, fekCellRange, fekCellOffset, 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 { Functions - they are identified by their name } fekFunc ); (* TFEKind = ( { Basic operands } fekCell, fekCellRef, fekCellRange, fekCellOffset, 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, { Built-in/Worksheet Functions} // math fekABS, fekACOS, fekACOSH, fekASIN, fekASINH, fekATAN, fekATANH, fekCOS, fekCOSH, fekDEGREES, fekEXP, fekINT, fekLN, fekLOG, fekLOG10, fekPI, fekRADIANS, fekRAND, fekROUND, fekSIGN, fekSIN, fekSINH, fekSQRT, fekTAN, fekTANH, // date/time fekDATE, fekDATEDIF, fekDATEVALUE, fekDAY, fekHOUR, fekMINUTE, fekMONTH, fekNOW, fekSECOND, fekTIME, fekTIMEVALUE, fekTODAY, fekWEEKDAY, fekYEAR, // statistical fekAVEDEV, fekAVERAGE, fekBETADIST, fekBETAINV, fekBINOMDIST, fekCHIDIST, fekCHIINV, fekCOUNT, fekCOUNTA, fekCOUNTBLANK, fekCOUNTIF, fekMAX, fekMEDIAN, fekMIN, fekPERMUT, fekPOISSON, fekPRODUCT, fekSTDEV, fekSTDEVP, fekSUM, fekSUMIF, fekSUMSQ, fekVAR, fekVARP, // financial fekFV, fekNPER, fekPMT, fekPV, fekRATE, // logical fekAND, fekFALSE, fekIF, fekNOT, fekOR, fekTRUE, // string fekCHAR, fekCODE, fekLEFT, fekLOWER, fekMID, fekPROPER, fekREPLACE, fekRIGHT, fekSUBSTITUTE, fekTRIM, fekUPPER, // lookup/reference fekCOLUMN, fekCOLUMNS, fekROW, fekROWS, // info fekCELLINFO, fekINFO, fekIsBLANK, fekIsERR, fekIsERROR, fekIsLOGICAL, fekIsNA, fekIsNONTEXT, fekIsNUMBER, fekIsRef, fekIsTEXT, fekValue, { Other operations } fekOpSUM {Unary sum operation. Note: CANNOT be used for summing sell contents; use fekSUM} ); *) {@@ These tokens identify operands in RPN formulas. } TOperandTokens = fekCell..fekMissingArg; {@@ These tokens identify basic operations in RPN formulas. } TBasicOperationTokens = fekAdd..fekParen; (* {@@ These tokens identify spreadsheet functions in RPN formulas. } TFuncTokens = fekAbs..fekOpSum; *) {@@ Flags to mark the address or a cell or a range of cells to be absolute or relative. They are used in the set TsRelFlags. } TsRelFlag = (rfRelRow, rfRelCol, rfRelRow2, rfRelCol2); {@@ Flags to mark the address of a cell or a range of cells to be absolute or relative. It is a set consisting of TsRelFlag elements. } TsRelFlags = set of TsRelFlag; {@@ Elements of an expanded formula. 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 Param1, Param2: Word; // Extra parameters DoubleValue: double; IntValue: Word; StringValue: String; RelFlags: TsRelFlags; // store info on relative/absolute addresses FuncName: String; ParamsNum: Byte; end; {@@ RPN formula. Similar to the expanded formula, but in RPN notation. Simplifies the task of format writers which need RPN } TsRPNFormula = array of TsFormulaElement; {@@ Describes the type of content in a cell of a TsWorksheet } TCellContentType = (cctEmpty, cctFormula, cctNumber, cctUTF8String, cctDateTime, cctBool, cctError); {@@ Error code values } TsErrorValue = ( errOK, // no error errEmptyIntersection, // #NULL! errDivideByZero, // #DIV/0! errWrongType, // #VALUE! errIllegalRef, // #REF! errWrongName, // #NAME? errOverflow, // #NUM! errArgError, // #N/A // --- no Excel errors -- errFormulaNotSupported ); {@@ List of possible formatting fields } TsUsedFormattingField = (uffTextRotation, uffFont, uffBold, uffBorder, uffBackgroundColor, uffNumberFormat, uffWordWrap, uffHorAlign, uffVertAlign ); {@@ Describes which formatting fields are active } TsUsedFormattingFields = set of TsUsedFormattingField; {@@ Number/cell formatting. Only uses a subset of the default formats, enough to be able to read/write date/time values. nfCustom allows to apply a format string directly. } TsNumberFormat = ( // general-purpose for all numbers nfGeneral, // numbers nfFixed, nfFixedTh, nfExp, nfPercentage, // currency nfCurrency, nfCurrencyRed, // dates and times nfShortDateTime, {nfFmtDateTime, }nfShortDate, nfLongDate, nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval, // other (format string goes directly into the file) nfCustom); const { @@ Codes for curreny format according to FormatSettings.CurrencyFormat: "C" = currency symbol, "V" = currency value, "S" = space character For the negative value formats, we use also: "B" = bracket, "M" = Minus The order of these characters represents the order of these items. Example: 1000 dollars --> "$1000" for pCV, or "1000 $" for pVsC -1000 dollars --> "($1000)" for nbCVb, or "-$ 1000" for nMCSV Assignment taken from "sysstr.inc" } pcfDefault = -1; // use value from Worksheet.FormatSettings.CurrencyFormat pcfCV = 0; // $1000 pcfVC = 1; // 1000$ pcfCSV = 2; // $ 1000 pcfVSC = 3; // 1000 $ ncfDefault = -1; // use value from Worksheet.FormatSettings.NegCurrFormat ncfBCVB = 0; // ($1000) ncfMCV = 1; // -$1000 ncfCMV = 2; // $-1000 ncfCVM = 3; // $1000- ncfBVCB = 4; // (1000$) ccfMVC = 5; // -1000$ ncfVMC = 6; // 1000-$ ncfVCM = 7; // 1000$- ncfMVSC = 8; // -1000 $ ncfMCSV = 9; // -$ 1000 ncfVSCM = 10; // 1000 $- ncfCSVM = 11; // $ 1000- ncfCSMV = 12; // $ -1000 ncfVMSC = 13; // 1000- $ ncfBCSVB = 14; // ($ 1000) ncfBVSCB = 15; // (1000 $) type {@@ Text rotation formatting. The text is rotated relative to the standard orientation, which is from left to right horizontal:
---> ABCSo 90 degrees clockwise means that the text will be:
| A | B v CAnd 90 degree counter clockwise will be:
^ C | B | ADue to limitations of the text mode the characters are not rotated here. There is, however, also a "stacked" variant which looks exactly like the former case. } TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation, rt90DegreeCounterClockwiseRotation, rtStacked); {@@ Indicates horizontal text alignment in cells } TsHorAlignment = (haDefault, haLeft, haCenter, haRight); {@@ Indicates vertical text alignment in cells } TsVertAlignment = (vaDefault, vaTop, vaCenter, vaBottom); {@@ Colors in fpspreadsheet are given as indices into a palette. Use the workbook's GetPaletteColor to determine the color rgb value as little-endian (with "r" being the low-value byte, in agreement with TColor). The data type for rgb values is TsColorValue. } TsColor = Word; {@@ These are some constants for color indices into the default palette. Note, however, that if a different palette is used there may be more colors, and the names of the color constants may no longer be correct. } const scBlack = $00; scWhite = $01; scRed = $02; scGreen = $03; scBlue = $04; scYellow = $05; scMagenta = $06; scCyan = $07; scDarkRed = $08; scDarkGreen = $09; scDarkBlue = $0A; scNavy = $0A; scOlive = $0B; scPurple = $0C; scTeal = $0D; scSilver = $0E; scGrey = $0F; scGray = $0F; // redefine to allow different spelling scGrey10pct = $10; scGray10pct = $10; scGrey20pct = $11; scGray20pct = $11; scOrange = $12; scDarkbrown = $13; scBrown = $14; scBeige = $15; scWheat = $16; // not sure - but I think the mechanism with scRGBColor is not working... // Will be removed sooner or later... scRGBColor = $FFFF; scTransparent = $FFFE; scNotDefined = $FFFF; type {@@ Data type for rgb color values } TsColorValue = DWord; {@@ Palette of color values. A "color value" is a DWord value containing rgb colors. } TsPalette = array[0..0] of TsColorValue; PsPalette = ^TsPalette; {@@ Font style (redefined to avoid usage of "Graphics" } TsFontStyle = (fssBold, fssItalic, fssStrikeOut, fssUnderline); {@@ Set of font styles } TsFontStyles = set of TsFontStyle; {@@ Font record used in fpspreadsheet. Contains the font name, the font size (in points), the font style, and the font color. } TsFont = class {@@ Name of the font face, such as 'Arial' or 'Times New Roman' } FontName: String; {@@ Size of the font in points } Size: Single; // in "points" {@@ Font style, such as bold, italics etc. - see TsFontStyle} Style: TsFontStyles; {@@ Text color given by the index into the workbook's color palette } Color: TsColor; end; {@@ Indicates the border for a cell. If included in the CellBorders set the corresponding border is drawn in the style defined by the CellBorderStyle. } TsCellBorder = (cbNorth, cbWest, cbEast, cbSouth, cbDiagUp, cbDiagDown); {@@ Indicates the border for a cell } TsCellBorders = set of TsCellBorder; {@@ Line style (for cell borders) } TsLineStyle = (lsThin, lsMedium, lsDashed, lsDotted, lsThick, lsDouble, lsHair); {@@ The Cell border style reocrd contains the linestyle and color of a cell border. There is a CellBorderStyle for each border. } TsCellBorderStyle = record LineStyle: TsLineStyle; Color: TsColor; end; {@@ The cell border styles of each cell border are collected in this array. } TsCellBorderStyles = array[TsCellBorder] of TsCellBorderStyle; {@@ Border styles for each cell border used by default: a thin, black, solid line } const DEFAULT_BORDERSTYLES: TsCellBorderStyles = ( (LineStyle: lsThin; Color: scBlack), (LineStyle: lsThin; Color: scBlack), (LineStyle: lsThin; Color: scBlack), (LineStyle: lsThin; Color: scBlack), (LineStyle: lsThin; Color: scBlack), (LineStyle: lsThin; Color: scBlack) ); type {@@ Identifier for a compare operation } TsCompareOperation = (coNotUsed, coEqual, coNotEqual, coLess, coGreater, coLessEqual, coGreaterEqual ); {@@ State flags while calculating formulas } TsCalcState = (csNotCalculated, csCalculating, csCalculated); {@@ Record combining a cell's row and column indexes } TsCellCoord = record Row, Col: Cardinal; end; {@@ Record combining row and column cornder indexes of a range of cells } TsCellRange = record Row1, Col1, Row2, Col2: Cardinal; end; {@@ Pointer to a TCell record } PCell = ^TCell; {@@ Cell structure for TsWorksheet The cell record contains information on the location of the cell (row and column index), on the value contained (number, date, text, ...), and on formatting. Never suppose that all *Value fields are valid, only one of the ContentTypes is valid. For other fields use TWorksheet.ReadAsUTF8Text and similar methods @see ReadAsUTF8Text } TCell = record Col: Cardinal; // zero-based Row: Cardinal; // zero-based ContentType: TCellContentType; { Possible values for the cells } FormulaValue: string; NumberValue: double; UTF8StringValue: ansistring; DateTimeValue: TDateTime; BoolValue: Boolean; ErrorValue: TsErrorValue; SharedFormulaBase: PCell; // Cell containing the shared formula MergedNeighbors: TsCellBorders; { Formatting fields } { When adding/deleting formatting fields don't forget to update CopyFormat! } UsedFormattingFields: TsUsedFormattingFields; FontIndex: Integer; TextRotation: TsTextRotation; HorAlignment: TsHorAlignment; VertAlignment: TsVertAlignment; Border: TsCellBorders; BorderStyles: TsCelLBorderStyles; BackgroundColor: TsColor; NumberFormat: TsNumberFormat; NumberFormatStr: String; RGBBackgroundColor: TFPColor; // only valid if BackgroundColor=scRGBCOLOR { Status flags } CalcState: TsCalcState; end; const // Takes account of effect of cell margins on row height by adding this // value to the nominal row height. Note that this is an empirical value and may be wrong. ROW_HEIGHT_CORRECTION = 0.2; type {@@ The record TRow contains information about a spreadsheet row: @param Row The index of the row (beginning with 0) @param Height The height of the row (expressed as lines count of the default font) Only rows with heights that cannot be derived from the font height have a row record. } TRow = record Row: Cardinal; Height: Single; // in "lines" end; {@@ Pointer to a TRow record } PRow = ^TRow; {@@ The record TCol contains information about a spreadsheet column: @param Col The index of the column (beginning with 0) @param Width The width of the column (expressed in character count of the "0" character of the default font. Only columns with non-default widths have a column record. } TCol = record Col: Cardinal; Width: Single; // in "characters". Excel uses the width of char "0" in 1st font end; {@@ Pointer to a TCol record } PCol = ^TCol; {@@ WSorksheet user interface options: @param soShowGridLines Show or hide the grid lines in the spreadsheet @param soShowHeaders Show or hide the column or row headers of the spreadsheet @param soHasFrozenPanes If set a number of rows and columns of the spreadsheet is fixed and does not scroll. The number is defined by LeftPaneWidth and TopPaneHeight. } TsSheetOption = (soShowGridLines, soShowHeaders, soHasFrozenPanes); {@@ Set of user interface options @ see TsSheetOption } TsSheetOptions = set of TsSheetOption; type TsCustomSpreadReader = class; TsCustomSpreadWriter = class; TsWorkbook = class; { TsWorksheet } {@@ This event fires whenever a cell value or cell formatting changes. It is handled by TsWorksheetGrid to update the grid. } TsCellEvent = procedure (Sender: TObject; ARow, ACol: Cardinal) of object; {@@ The worksheet contains a list of cells and provides a variety of methods to read or write data to the cells, or to change their formatting. } TsWorksheet = class private FWorkbook: TsWorkbook; FCells: TAvlTree; // Items are TCell FCurrentNode: TAVLTreeNode; // For GetFirstCell and GetNextCell FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default FLeftPaneWidth: Integer; FTopPaneHeight: Integer; FOptions: TsSheetOptions; FFirstRowIndex: Cardinal; FFirstColIndex: Cardinal; FLastRowIndex: Cardinal; FLastColIndex: Cardinal; FDefaultColWidth: Single; // in "characters". Excel uses the width of char "0" in 1st font FDefaultRowHeight: Single; // in "character heights", i.e. line count FOnChangeCell: TsCellEvent; FOnChangeFont: TsCellEvent; { Setter/Getter } function GetFormatSettings: TFormatSettings; { Callback procedures called when iterating through all cells } procedure CalcFormulaCallback(data, arg: Pointer); procedure CalcStateCallback(data, arg: Pointer); procedure InsertColCallback(data, arg: Pointer); procedure InsertRowCallback(data, arg: Pointer); procedure RemoveCallback(data, arg: pointer); protected function CellUsedInFormula(ARow, ACol: Cardinal): Boolean; procedure ChangedCell(ARow, ACol: Cardinal); procedure ChangedFont(ARow, ACol: Cardinal); public {@@ Name of the sheet. In the popular spreadsheet applications this is displayed at the tab of the sheet. } Name: string; { Base methods } constructor Create; destructor Destroy; override; { Utils } class function CellPosToText(ARow, ACol: Cardinal): string; procedure RemoveAllCells; procedure UpdateCaches; { Reading of values } function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload; function ReadAsUTF8Text(ACell: PCell): ansistring; overload; function ReadAsNumber(ARow, ACol: Cardinal): Double; overload; function ReadAsNumber(ACell: PCell): Double; overload; function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload; function ReadAsDateTime(ACell: PCell; out AResult: TDateTime): Boolean; overload; function ReadFormulaAsString(ACell: PCell; ALocalized: Boolean = false): String; function ReadNumericValue(ACell: PCell; out AValue: Double): Boolean; { Reading of cell attributes } function GetNumberFormatAttributes(ACell: PCell; out ADecimals: Byte; out ACurrencySymbol: String): Boolean; function ReadUsedFormatting(ARow, ACol: Cardinal): TsUsedFormattingFields; function ReadBackgroundColor(ARow, ACol: Cardinal): TsColor; { Merging of cells } procedure MergeCells(ARow1, ACol1, ARow2, ACol2: Cardinal); overload; procedure MergeCells(ARange: String); overload; procedure UnmergeCells(ARow1, ACol1, ARow2, ACol2: Cardinal); overload; procedure UnmergeCells(ARange: String); overload; function FindMergeBase(ACell: PCell): PCell; function FindMergedRange(ACell: PCell; out ARow1, ACol1, ARow2, ACol2: Cardinal): Boolean; { Writing of values } function WriteBlank(ARow, ACol: Cardinal): PCell; overload; procedure WriteBlank(ACell: PCell); overload; function WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean): PCell; overload; procedure WriteBoolValue(ACell: PCell; AValue: Boolean); overload; function WriteCellValueAsString(ARow, ACol: Cardinal; AValue: String): PCell; overload; procedure WriteCellValueAsString(ACell: PCell; AValue: String); overload; function WriteCurrency(ARow, ACol: Cardinal; AValue: Double; AFormat: TsNumberFormat = nfCurrency; ADecimals: Integer = 2; ACurrencySymbol: String = '?'; APosCurrFormat: Integer = -1; ANegCurrFormat: Integer = -1): PCell; overload; procedure WriteCurrency(ACell: PCell; AValue: Double; AFormat: TsNumberFormat = nfCurrency; ADecimals: Integer = -1; ACurrencySymbol: String = '?'; APosCurrFormat: Integer = -1; ANegCurrFormat: Integer = -1); overload; function WriteCurrency(ARow, ACol: Cardinal; AValue: Double; AFormat: TsNumberFormat; AFormatString: String): PCell; overload; procedure WriteCurrency(ACell: PCell; AValue: Double; AFormat: TsNumberFormat; AFormatString: String); overload; function WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''): PCell; overload; procedure WriteDateTime(ACell: PCell; AValue: TDateTime; AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); overload; function WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; AFormatStr: String): PCell; overload; procedure WriteDateTime(ACell: PCell; AValue: TDateTime; AFormatStr: String); overload; function WriteErrorValue(ARow, ACol: Cardinal; AValue: TsErrorValue): PCell; overload; procedure WriteErrorValue(ACell: PCell; AValue: TsErrorValue); overload; function WriteFormula(ARow, ACol: Cardinal; AFormula: String; ALocalized: Boolean = false): PCell; overload; procedure WriteFormula(ACell: PCell; AFormula: String; ALocalized: Boolean = false); overload; function WriteNumber(ARow, ACol: Cardinal; ANumber: double): PCell; overload; procedure WriteNumber(ACell: PCell; ANumber: Double); overload; function WriteNumber(ARow, ACol: Cardinal; ANumber: double; AFormat: TsNumberFormat; ADecimals: Byte = 2): PCell; overload; procedure WriteNumber(ACell: PCell; ANumber: Double; AFormat: TsNumberFormat; ADecimals: Byte = 2); overload; function WriteNumber(ARow, ACol: Cardinal; ANumber: double; AFormat: TsNumberFormat; AFormatString: String): PCell; overload; procedure WriteNumber(ACell: PCell; ANumber: Double; AFormat: TsNumberFormat; AFormatString: String); overload; function WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula): PCell; overload; procedure WriteRPNFormula(ACell: PCell; AFormula: TsRPNFormula); overload; procedure WriteSharedFormula(ARow1, ACol1, ARow2, ACol2: Cardinal; const AFormula: String); overload; procedure WriteSharedFormula(ACellRange: String; const AFormula: String); overload; function WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring): PCell; overload; procedure WriteUTF8Text(ACell: PCell; AText: ansistring); overload; { Writing of cell attributes } function WriteBackgroundColor(ARow, ACol: Cardinal; AColor: TsColor): PCell; overload; procedure WriteBackgroundColor(ACell: PCell; AColor: TsColor); overload; function WriteBorderColor(ARow, ACol: Cardinal; ABorder: TsCellBorder; AColor: TsColor): PCell; overload; procedure WriteBorderColor(ACell: PCell; ABorder: TsCellBorder; AColor: TsColor); overload; function WriteBorderLineStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; ALineStyle: TsLineStyle): PCell; overload; procedure WriteBorderLineStyle(ACell: PCell; ABorder: TsCellBorder; ALineStyle: TsLineStyle); overload; function WriteBorders(ARow, ACol: Cardinal; ABorders: TsCellBorders): PCell; overload; procedure WriteBorders(ACell: PCell; ABorders: TsCellBorders); overload; function WriteBorderStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; AStyle: TsCellBorderStyle): PCell; overload; procedure WriteBorderStyle(ACell: PCell; ABorder: TsCellBorder; AStyle: TsCellBorderStyle); overload; function WriteBorderStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; ALineStyle: TsLineStyle; AColor: TsColor): PCell; overload; procedure WriteBorderStyle(ACell: PCell; ABorder: TsCellBorder; ALineStyle: TsLineStyle; AColor: TsColor); overload; function WriteBorderStyles(ARow, ACol: Cardinal; const AStyles: TsCellBorderStyles): PCell; overload; procedure WriteBorderStyles(ACell: PCell; const AStyles: TsCellBorderStyles); overload; function WriteDateTimeFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat; const AFormatString: String = ''): PCell; overload; procedure WriteDateTimeFormat(ACell: PCell; ANumberFormat: TsNumberFormat; const AFormatString: String = ''); overload; function WriteDecimals(ARow, ACol: Cardinal; ADecimals: byte): PCell; overload; procedure WriteDecimals(ACell: PCell; ADecimals: Byte); overload; function WriteFont(ARow, ACol: Cardinal; const AFontName: String; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload; function WriteFont(ACell: PCell; const AFontName: String; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload; function WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer): PCell; overload; procedure WriteFont(ACell: PCell; AFontIndex: Integer); overload; function WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer; overload; function WriteFontColor(ACell: PCell; AFontColor: TsColor): Integer; overload; function WriteFontName(ARow, ACol: Cardinal; AFontName: String): Integer; overload; function WriteFontName(ACell: PCell; AFontName: String): Integer; overload; function WriteFontSize(ARow, ACol: Cardinal; ASize: Single): Integer; overload; function WriteFontSize(ACell: PCell; ASize: Single): Integer; overload; function WriteFontStyle(ARow, ACol: Cardinal; AStyle: TsFontStyles): Integer; overload; function WriteFontStyle(ACell: PCell; AStyle: TsFontStyles): Integer; overload; function WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment): PCell; overload; procedure WriteHorAlignment(ACell: PCell; AValue: TsHorAlignment); overload; function WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat; const AFormatString: String = ''): PCell; overload; procedure WriteNumberFormat(ACell: PCell; ANumberFormat: TsNumberFormat; const AFormatString: String = ''); overload; function WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat; ADecimals: Integer; ACurrencySymbol: String = ''; APosCurrFormat: Integer = -1; ANegCurrFormat: Integer = -1): PCell; overload; procedure WriteNumberFormat(ACell: PCell; ANumberFormat: TsNumberFormat; ADecimals: Integer; ACurrencySymbol: String = ''; APosCurrFormat: Integer = -1; ANegCurrFormat: Integer = -1); overload; function WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation): PCell; overload; procedure WriteTextRotation(ACell: PCell; ARotation: TsTextRotation); overload; procedure WriteUsedFormatting(ARow, ACol: Cardinal; AUsedFormatting: TsUsedFormattingFields); function WriteVertAlignment(ARow, ACol: Cardinal; AValue: TsVertAlignment): PCell; overload; procedure WriteVertAlignment(ACell: PCell; AValue: TsVertAlignment); overload; function WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean): PCell; overload; procedure WriteWordwrap(ACell: PCell; AValue: boolean); overload; { Formulas } function BuildRPNFormula(ACell: PCell): TsRPNFormula; procedure CalcFormula(ACell: PCell); procedure CalcFormulas; function ConvertRPNFormulaToStringFormula(const AFormula: TsRPNFormula): String; function UseSharedFormula(ARow, ACol: Cardinal; ASharedFormulaBase: PCell): PCell; { Data manipulation methods - For Cells } procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet); procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); overload; procedure CopyFormat(AFromCell, AToCell: PCell); overload; function FindCell(ARow, ACol: Cardinal): PCell; overload; function FindCell(AddressStr: String): PCell; overload; function GetCell(ARow, ACol: Cardinal): PCell; overload; function GetCell(AddressStr: String): PCell; overload; function GetCellCount: Cardinal; function GetFirstCell(): PCell; function GetNextCell(): PCell; function GetFirstCellOfRow(ARow: Cardinal): PCell; function GetLastCellOfRow(ARow: Cardinal): PCell; function GetFirstColIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastColIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex'; function GetLastOccupiedColIndex: Cardinal; function GetFirstRowIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastOccupiedRowIndex: Cardinal; function GetLastRowIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastRowNumber: Cardinal; deprecated 'Use GetLastRowIndex'; { Data manipulation methods - For Rows and Cols } function CalcAutoRowHeight(ARow: Cardinal): Single; function FindRow(ARow: Cardinal): PRow; function FindCol(ACol: Cardinal): PCol; function GetCellCountInRow(ARow: Cardinal): Cardinal; function GetCellCountInCol(ACol: Cardinal): Cardinal; function GetRow(ARow: Cardinal): PRow; function GetRowHeight(ARow: Cardinal): Single; function GetCol(ACol: Cardinal): PCol; function GetColWidth(ACol: Cardinal): Single; procedure InsertCol(ACol: Cardinal); procedure InsertRow(ARow: Cardinal); procedure RemoveAllRows; procedure RemoveAllCols; procedure RemoveCol(ACol: Cardinal); procedure RemoveRow(ARow: Cardinal); procedure WriteRowInfo(ARow: Cardinal; AData: TRow); procedure WriteRowHeight(ARow: Cardinal; AHeight: Single); procedure WriteColInfo(ACol: Cardinal; AData: TCol); procedure WriteColWidth(ACol: Cardinal; AWidth: Single); { Properties } {@@ List of cells of the worksheet. Only cells with contents or with formatting are listed } property Cells: TAVLTree read FCells; {@@ List of all column records of the worksheet having a non-standard column width } property Cols: TIndexedAVLTree read FCols; {@@ FormatSettings for localization of some formatting strings } property FormatSettings: TFormatSettings read GetFormatSettings; {@@ List of all row records of the worksheet having a non-standard row height } property Rows: TIndexedAVLTree read FRows; {@@ Workbook to which the worksheet belongs } property Workbook: TsWorkbook read FWorkbook; {@@ The default column width given in "character units" (width of the character "0" in the default font) } property DefaultColWidth: Single read FDefaultColWidth write FDefaultColWidth; {@@ The default row height is given in "line count" (height of the default font } property DefaultRowHeight: Single read FDefaultRowHeight write FDefaultRowHeight; // These are properties to interface to TsWorksheetGrid {@@ Parameters controlling visibility of grid lines and row/column headers, usage of frozen panes etc. } property Options: TsSheetOptions read FOptions write FOptions; {@@ Number of frozen columns which do not scroll } property LeftPaneWidth: Integer read FLeftPaneWidth write FLeftPaneWidth; {@@ Number of frozen rows which do not scroll } property TopPaneHeight: Integer read FTopPaneHeight write FTopPaneHeight; {@@ Event fired when cell contents or formatting changes } property OnChangeCell: TsCellEvent read FOnChangeCell write FOnChangeCell; {@@ Event fired when the font size in a cell changes } property OnChangeFont: TsCellEvent read FOnChangeFont write FOnChangeFont; end; {@@ Option flags for the workbook @param boVirtualMode If in virtual mode date are not taken from cells when a spreadsheet is written to file, but are provided by means of the event OnWriteCellData. Similarly, when data are read they are not added as cells but passed the the event OnReadCellData; @param boBufStream When this option is set a buffered stream is used for writing (a memory stream swapping to disk) or reading (a file stream pre-reading chunks of data to memory) @param boAutoCalc Automatically recalculate rpn formulas whenever a cell value changes. @param boCalcBeforeSaving Calculates formulas before saving the file. Otherwise there are no results when the file is loaded back by fpspreadsheet. @param boReadFormulas Allows to turn off reading of rpn formulas; this is a precaution since formulas not correctly implemented by fpspreadsheet could crash the reading operation. } TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc, boCalcBeforeSaving, boReadFormulas); {@@ Set of options flags for the workbook } TsWorkbookOptions = set of TsWorkbookOption; {@@ Event fired when writing a file in virtual mode. The event handler has to pass data ("AValue") and formatting style to be copied from a template cell ("AStyleCell") to the writer } TsWorkbookWriteCellDataEvent = procedure(Sender: TObject; ARow, ACol: Cardinal; var AValue: variant; var AStyleCell: PCell) of object; {@@ Event fired when reading a file in virtual mode. Read data are provided in the "ADataCell" (which is not added to the worksheet in virtual mode). } TsWorkbookReadCellDataEvent = procedure(Sender: TObject; ARow, ACol: Cardinal; const ADataCell: PCell) of object; {@@ The workbook contains the worksheets and provides methods for reading from and writing to file. } TsWorkbook = class private { Internal data } FWorksheets: TFPList; FEncoding: TsEncoding; FFormat: TsSpreadsheetFormat; FFontList: TFPList; FBuiltinFontCount: Integer; FPalette: array of TsColorValue; FVirtualColCount: Cardinal; FVirtualRowCount: Cardinal; FWriting: Boolean; FCalculationLock: Integer; FOptions: TsWorkbookOptions; FOnWriteCellData: TsWorkbookWriteCellDataEvent; FOnReadCellData: TsWorkbookReadCellDataEvent; FFileName: String; FLog: TStringList; { Setter/Getter } function GetErrorMsg: String; procedure SetVirtualColCount(AValue: Cardinal); procedure SetVirtualRowCount(AValue: Cardinal); { Internal methods } procedure GetLastRowColIndex(out ALastRow, ALastCol: Cardinal); procedure PrepareBeforeReading; procedure PrepareBeforeSaving; procedure ReCalc; procedure RemoveWorksheetsCallback(data, arg: pointer); procedure UpdateCaches; public {@@ A copy of SysUtil's DefaultFormatSettings to provide some kind of localization to some formatting strings. Can be modified before loading/writing files } FormatSettings: TFormatSettings; { Base methods } constructor Create; destructor Destroy; override; class function GetFormatFromFileName(const AFileName: TFileName; out SheetType: TsSpreadsheetFormat): Boolean; function CreateSpreadReader(AFormat: TsSpreadsheetFormat): TsCustomSpreadReader; function CreateSpreadWriter(AFormat: TsSpreadsheetFormat): TsCustomSpreadWriter; procedure ReadFromFile(AFileName: string; AFormat: TsSpreadsheetFormat); overload; procedure ReadFromFile(AFileName: string); overload; procedure ReadFromFileIgnoringExtension(AFileName: string); procedure ReadFromStream(AStream: TStream; AFormat: TsSpreadsheetFormat); procedure WriteToFile(const AFileName: string; const AFormat: TsSpreadsheetFormat; const AOverwriteExisting: Boolean = False); overload; procedure WriteToFile(const AFileName: String; const AOverwriteExisting: Boolean = False); overload; procedure WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat); { Worksheet list handling methods } function AddWorksheet(AName: string): TsWorksheet; function GetFirstWorksheet: TsWorksheet; function GetWorksheetByIndex(AIndex: Cardinal): TsWorksheet; function GetWorksheetByName(AName: String): TsWorksheet; function GetWorksheetCount: Cardinal; procedure RemoveAllWorksheets; { Font handling } function AddFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles; AColor: TsColor): Integer; overload; function AddFont(const AFont: TsFont): Integer; overload; procedure CopyFontList(ASource: TFPList); procedure DeleteFont(AFontIndex: Integer); function FindFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles; AColor: TsColor): Integer; function GetDefaultFont: TsFont; function GetDefaultFontSize: Single; function GetFont(AIndex: Integer): TsFont; function GetFontAsString(AIndex: Integer): String; function GetFontCount: Integer; procedure InitFonts; procedure RemoveAllFonts; procedure SetDefaultFont(const AFontName: String; ASize: Single); { Color handling } function AddColorToPalette(AColorValue: TsColorValue): TsColor; function FindClosestColor(AColorValue: TsColorValue; AMaxPaletteCount: Integer): TsColor; function FPSColorToHexString(AColor: TsColor; ARGBColor: TFPColor): String; function GetColorName(AColorIndex: TsColor): string; function GetPaletteColor(AColorIndex: TsColor): TsColorValue; function GetPaletteColorAsHTMLStr(AColorIndex: TsColor): String; procedure SetPaletteColor(AColorIndex: TsColor; AColorValue: TsColorValue); function GetPaletteSize: Integer; procedure UseDefaultPalette; procedure UsePalette(APalette: PsPalette; APaletteCount: Word; ABigEndian: Boolean = false); function UsesColor(AColorIndex: TsColor): Boolean; { Error messages } procedure AddErrorMsg(const AMsg: String); overload; procedure AddErrorMsg(const AMsg: String; const Args: array of const); overload; procedure ClearErrorList; {@@ This property is only used for formats which don't support unicode and support a single encoding for the whole document, like Excel 2 to 5 } property Encoding: TsEncoding read FEncoding write FEncoding; {@@ Retrieves error messages collected during reading/writing } property ErrorMsg: String read GetErrorMsg; {@@ Filename of the saved workbook } property FileName: String read FFileName; {@@ Identifies the file format which was detected when reading the file } property FileFormat: TsSpreadsheetFormat read FFormat; property VirtualColCount: cardinal read FVirtualColCount write SetVirtualColCount; property VirtualRowCount: cardinal read FVirtualRowCount write SetVirtualRowCount; property Options: TsWorkbookOptions read FOptions write FOptions; {@@ This event allows to provide external cell data for writing to file, standard cells are ignored. Intended for converting large database files to a spreadsheet format. Requires Option boVirtualMode to be set. } property OnWriteCellData: TsWorkbookWriteCellDataEvent read FOnWriteCellData write FOnWriteCellData; {@@ This event accepts cell data while reading a spreadsheet file. Data are not encorporated in a spreadsheet, they are just passed through to the event handler for processing. Requires option boVirtualMode to be set. } property OnReadCellData: TsWorkbookReadCellDataEvent read FOnReadCellData write FOnReadCellData; end; {@@ Contents of a number format record } TsNumFormatData = class public {@@ Excel refers to a number format by means of the format "index". } Index: Integer; {@@ OpenDocument refers to a number format by means of the format "name". } Name: String; {@@ Identifier of a built-in number format, see TsNumberFormat } NumFormat: TsNumberFormat; {@@ String of format codes, such as '#,##0.00', or 'hh:nn'. } FormatString: string; end; {@@ Specialized list for number format items } TsCustomNumFormatList = class(TFPList) private function GetItem(AIndex: Integer): TsNumFormatData; procedure SetItem(AIndex: Integer; AValue: TsNumFormatData); protected {@@ Workbook from which the number formats are collected in the list. It is mainly needed to get access to the FormatSettings for easy localization of some formatting strings. } FWorkbook: TsWorkbook; {@@ Identifies the first number format item that is written to the file. Items having a smaller index are not written. } FFirstFormatIndexInFile: Integer; {@@ Identifies the index of the next Excel number format item to be written. Needed for auto-creating of the user-defined Excel number format indexes } FNextFormatIndex: Integer; procedure AddBuiltinFormats; virtual; procedure RemoveFormat(AIndex: Integer); public constructor Create(AWorkbook: TsWorkbook); destructor Destroy; override; function AddFormat(AFormatCell: PCell): Integer; overload; function AddFormat(AFormatIndex: Integer; AFormatName, AFormatString: String; ANumFormat: TsNumberFormat): Integer; overload; function AddFormat(AFormatIndex: Integer; AFormatString: String; ANumFormat: TsNumberFormat): Integer; overload; function AddFormat(AFormatName, AFormatString: String; ANumFormat: TsNumberFormat): Integer; overload; function AddFormat(AFormatString: String; ANumFormat: TsNumberFormat): Integer; overload; procedure AnalyzeAndAdd(AFormatIndex: Integer; AFormatString: String); procedure Clear; procedure ConvertAfterReading(AFormatIndex: Integer; var AFormatString: String; var ANumFormat: TsNumberFormat); virtual; procedure ConvertBeforeWriting(var AFormatString: String; var ANumFormat: TsNumberFormat); virtual; procedure Delete(AIndex: Integer); function Find(ANumFormat: TsNumberFormat; AFormatString: String): Integer; overload; function Find(AFormatString: String): Integer; overload; function FindByIndex(AFormatIndex: Integer): Integer; function FindByName(AFormatName: String): Integer; function FindFormatOf(AFormatCell: PCell): integer; virtual; function FormatStringForWriting(AIndex: Integer): String; virtual; procedure Sort; {@@ Workbook from which the number formats are collected in the list. It is mainly needed to get access to the FormatSettings for easy localization of some formatting strings. } property Workbook: TsWorkbook read FWorkbook; {@@ Identifies the first number format item that is written to the file. Items having a smaller index are not written. } property FirstFormatIndexInFile: Integer read FFirstFormatIndexInFile; {@@ Number format items contained in the list } property Items[AIndex: Integer]: TsNumFormatData read GetItem write SetItem; default; end; { TsCustomSpreadReaderWriter } {@@ Common ancestor of the spreadsheet reader and writer classes providing shared data and methods. } TsCustomSpreadReaderWriter = class protected {@@ Instance of the workbook which is currently being read. } FWorkbook: TsWorkbook; {@@ Instance of the worksheet which is currently being read. } FWorksheet: TsWorksheet; {@@ Limitations for the specific data file format } FLimitations: TsSpreadsheetFormatLimitations; protected {@@ List of number formats found in the file } FNumFormatList: TsCustomNumFormatList; procedure CreateNumFormatList; virtual; public constructor Create(AWorkbook: TsWorkbook); virtual; // to allow descendents to override it destructor Destroy; override; function Limitations: TsSpreadsheetFormatLimitations; {@@ Instance of the workbook which is currently being read/written. } property Workbook: TsWorkbook read FWorkbook; {@@ List of number formats found in the workbook. } property NumFormatList: TsCustomNumFormatList read FNumFormatList; end; { TsCustomSpreadReader } {@@ TsSpreadReader class reference type } TsSpreadReaderClass = class of TsCustomSpreadReader; {@@ Custom reader of spreadsheet files. "Custom" means that it provides only the basic functionality. The main implementation is done in derived classes for each individual file format. } TsCustomSpreadReader = class(TsCustomSpreadReaderWriter) protected {@@ Temporary cell for virtual mode} FVirtualCell: TCell; {@@ Stores if the reader is in virtual mode } FIsVirtualMode: Boolean; { Helper methods } {@@ Removes column records if all of them have the same column width } procedure FixCols(AWorksheet: TsWorksheet); {@@ Removes row records if all of them have the same row height } procedure FixRows(AWorksheet: TsWorksheet); { Record reading methods } {@@ Abstract method for reading a blank cell. Must be overridden by descendent classes. } procedure ReadBlank(AStream: TStream); virtual; abstract; {@@ Abstract method for reading a formula cell. Must be overridden by descendent classes. } procedure ReadFormula(AStream: TStream); virtual; abstract; {@@ Abstract method for reading a text cell. Must be overridden by descendent classes. } procedure ReadLabel(AStream: TStream); virtual; abstract; {@@ Abstract method for reading a number cell. Must be overridden by descendent classes. } procedure ReadNumber(AStream: TStream); virtual; abstract; public constructor Create(AWorkbook: TsWorkbook); override; { General writing methods } procedure ReadFromFile(AFileName: string; AData: TsWorkbook); virtual; procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); virtual; procedure ReadFromStrings(AStrings: TStrings; AData: TsWorkbook); virtual; end; { TsCustomSpreadWriter } {@@ TsSpreadWriter class reference type } TsSpreadWriterClass = class of TsCustomSpreadWriter; {@@ Callback function when iterating cells while accessing a stream } TCellsCallback = procedure (ACell: PCell; AStream: TStream) of object; {@@ Custom writer of spreadsheet files. "Custom" means that it provides only the basic functionality. The main implementation is done in derived classes for each individual file format. } TsCustomSpreadWriter = class(TsCustomSpreadReaderWriter) protected { Helper routines } procedure AddDefaultFormats(); virtual; procedure CheckLimitations; function FindFormattingInList(AFormat: PCell): Integer; procedure FixCellColors(ACell: PCell); function FixColor(AColor: TsColor): TsColor; virtual; procedure FixFormat(ACell: PCell); virtual; procedure GetSheetDimensions(AWorksheet: TsWorksheet; out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual; procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); procedure ListAllFormattingStyles; virtual; procedure ListAllNumFormatsCallback(ACell: PCell; AStream: TStream); procedure ListAllNumFormats; virtual; { Helpers for writing } procedure WriteCellCallback(ACell: PCell; AStream: TStream); procedure WriteCellsToStream(AStream: TStream; ACells: TAVLTree); { Record writing methods } {@@ Abstract method for writing a blank cell. Must be overridden by descendent classes. } procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual; abstract; {@@ Abstract method for writing a date/time value to a cell. Must be overridden by descendent classes. } procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); virtual; abstract; {@@ Abstract method for writing a formula to a cell. Must be overridden by descendent classes. } procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual; (* {@@ Abstract method for writing an RPN formula to a cell. Must be overridden by descendent classes. } procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); virtual; *) {@@ Abstract method for writing a string to a cell. Must be overridden by descendent classes. } procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); virtual; abstract; {@@ Abstract method for writing a number value to a cell. Must be overridden by descendent classes. } procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); virtual; abstract; public {@@ An array with cells which are models for the used styles In this array the Row property holds the index to the corresponding XF field } FFormattingStyles: array of TCell; {@@ Indicates which should be the next XF (style) index when filling the FFormattingStyles array } NextXFIndex: Integer; public constructor Create(AWorkbook: TsWorkbook); override; { General writing methods } procedure IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback); procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); virtual; procedure WriteToStream(AStream: TStream); virtual; procedure WriteToStrings(AStrings: TStrings); virtual; end; {@@ List of registered formats } TsSpreadFormatData = record ReaderClass: TsSpreadReaderClass; WriterClass: TsSpreadWriterClass; Format: TsSpreadsheetFormat; end; { Simple creation an RPNFormula array to be used in fpspreadsheet. } {@@ Helper record for simplification of RPN formula creation } PRPNItem = ^TRPNItem; TRPNItem = record FE: TsFormulaElement; Next: PRPNItem; end; function CreateRPNFormula(AItem: PRPNItem; AReverse: Boolean = false): TsRPNFormula; procedure DestroyRPNFormula(AItem: PRPNItem); function RPNBool(AValue: Boolean; ANext: PRPNItem): PRPNItem; function RPNCellValue(ACellAddress: String; ANext: PRPNItem): PRPNItem; overload; function RPNCellValue(ARow, ACol: Integer; AFlags: TsRelFlags; ANext: PRPNItem): PRPNItem; overload; function RPNCellRef(ACellAddress: String; ANext: PRPNItem): PRPNItem; overload; function RPNCellRef(ARow, ACol: Integer; AFlags: TsRelFlags; ANext: PRPNItem): PRPNItem; overload; function RPNCellRange(ACellRangeAddress: String; ANext: PRPNItem): PRPNItem; overload; function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags; ANext: PRPNItem): PRPNItem; overload; function RPNCellOffset(ARowOffset, AColOffset: Integer; AFlags: TsRelFlags; ANext: PRPNItem): PRPNItem; function RPNErr(AErrCode: Byte; ANext: PRPNItem): PRPNItem; function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem; function RPNMissingArg(ANext: PRPNItem): PRPNItem; function RPNNumber(AValue: Double; ANext: PRPNItem): PRPNItem; function RPNParenthesis(ANext: PRPNItem): PRPNItem; function RPNString(AValue: String; ANext: PRPNItem): PRPNItem; function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem; overload; function RPNFunc(AFuncName: String; ANext: PRPNItem): PRPNItem; overload; function RPNFunc(AFuncName: String; ANumParams: Byte; ANext: PRPNItem): PRPNItem; overload; // function FixedParamCount(AElementKind: TFEKind): Boolean; var GsSpreadFormats: array of TsSpreadFormatData; //procedure RegisterFormulaFunc(AFormulaKind: TFEKind; AFunc: pointer); procedure RegisterSpreadFormat( AReaderClass: TsSpreadReaderClass; AWriterClass: TsSpreadWriterClass; AFormat: TsSpreadsheetFormat); procedure CopyCellFormat(AFromCell, AToCell: PCell); function GetFileFormatName(AFormat: TsSpreadsheetFormat): String; procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer); function SameCellBorders(ACell1, ACell2: PCell): Boolean; procedure InitCell(out ACell: TCell); overload; procedure InitCell(ARow, ACol: Cardinal; out ACell: TCell); overload; function HasFormula(ACell: PCell): Boolean; implementation uses Math, StrUtils, TypInfo, fpsStreams, fpsUtils, fpsNumFormatParser, fpsExprParser; { Translatable strings } resourcestring lpUnsupportedReadFormat = 'Tried to read a spreadsheet using an unsupported format'; lpUnsupportedWriteFormat = 'Tried to write a spreadsheet using an unsupported format'; lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file'; lpUnknownSpreadsheetFormat = 'unknown format'; lpMaxRowsExceeded = 'This workbook contains %d rows, but the selected ' + 'file format does not support more than %d rows.'; lpMaxColsExceeded = 'This workbook contains %d columns, but the selected ' + 'file format does not support more than %d columns.'; lpTooManyPaletteColors = 'This workbook contains more colors (%d) than ' + 'supported by the file format (%d). The additional colors are replaced by '+ 'the best-matching palette colors.'; lpInvalidFontIndex = 'Invalid font index'; lpInvalidNumberFormat = 'Trying to use an incompatible number format.'; lpInvalidDateTimeFormat = 'Trying to use an incompatible date/time format.'; lpNoValidNumberFormatString = 'No valid number format string.'; lpNoValidCellAddress = '"%s" is not a valid cell address.'; lpNoValidCellRangeAddress = '"%s" is not a valid cell range address.'; lpNoValidCellRangeOrCellAddress = '"%s" is not a valid cell or cell range address.'; lpSpecifyNumberOfParams = 'Specify number of parameters for function %s'; lpIncorrectParamCount = 'Funtion %s requires at least %d and at most %d parameters.'; lpCircularReference = 'Circular reference found when calculating worksheet formulas'; lpTRUE = 'TRUE'; lpFALSE = 'FALSE'; lpErrEmptyIntersection = '#NULL!'; lpErrDivideByZero = '#DIV/0!'; lpErrWrongType = '#VALUE!'; lpErrIllegalRef = '#REF!'; lpErrWrongName = '#NAME?'; lpErrOverflow = '#NUM!'; lpErrArgError = '#N/A'; lpErrFormulaNotSupported = '
var f: TsRPNFormula; begin f := CreateRPNFormula( RPNCellValue('$A1', RPNNumber(2, RPNFunc(fekAdd, nil))));} function CreateRPNFormula(AItem: PRPNItem; AReverse: Boolean = false): TsRPNFormula; var item: PRPNItem; nextitem: PRPNItem; n: Integer; begin // Determine count of RPN elements n := 0; item := AItem; while item <> nil do begin inc(n); item := item^.Next; end; // Set array length of TsRPNFormula result SetLength(Result, n); // Copy FormulaElements to result and free temporary RPNItems item := AItem; if AReverse then n := Length(Result)-1 else n := 0; while item <> nil do begin nextitem := item^.Next; Result[n] := item^.FE; if AReverse then dec(n) else inc(n); DisposeRPNItem(item); item := nextitem; end; end; {@@ Destroys the RPN formula starting with the given RPN item. @param AItem Pointer to the first RPN items representing the formula. Each item contains a pointer to the next item in the list. The list is terminated by nil. } procedure DestroyRPNFormula(AItem: PRPNItem); var nextitem: PRPNItem; begin while AItem <> nil do begin nextitem := AItem^.Next; DisposeRPNItem(AItem); AItem := nextitem; end; end; initialization MakeLEPalette(@DEFAULT_PALETTE, Length(DEFAULT_PALETTE)); finalization SetLength(GsSpreadFormats, 0); end. { Strategy for handling of number formats: Problem: For number formats, fpspreadsheet uses a syntax which is slightly different from the syntax that Excel uses in the xls files. Moreover, the file syntax can be different from file type to file type (biff2, for example, allows only a few predefined formats, while the number of allowed formats is unlimited (?) for biff8. Number format handling in fpspreadsheet is implemented with the following concept in mind: - Formats written into TsWorksheet cells always follow the fpspreadsheet syntax. - For writing, the writer creates a TsNumFormatList which stores all formats in file syntax. - The built-in formats of the file types are coded in the fpc syntax. - The method "ConvertBeforeWriting" converts the cell formats from the fpspreadsheet to the file syntax. - For reading, the reader creates another TsNumFormatList. - The built-in formats of the file types are coded again in fpc syntax. - After reading, the formats are converted to fpc syntax by means of "ConvertAfterReading". - Format conversion is done internally by means of the TsNumFormatParser. }