2008-02-24 13:18:34 +00:00
{
2009-02-05 10:21:42 +00:00
xlsbiff8. pas
2008-02-24 13:18:34 +00:00
2009-02-05 10:21:42 +00:00
Writes an Excel 8 file
2008-02-24 13:18:34 +00:00
An Excel worksheet stream consists of a number of subsequent records.
To ensure a properly formed file , the following order must be respected:
1 st record : BOF
2 nd to Nth record : Any record
Last record : EOF
2009-09-01 16:47:45 +00:00
Excel 8 files are OLE compound document files, and must be written using the
2008-02-24 13:18:34 +00:00
fpOLE library .
2009-04-21 15:08:43 +00:00
Records Needed to Make a BIFF8 File Microsoft Excel Can Use:
2008-02-24 13:18:34 +00:00
Required Records:
BOF - Set the 6 byte offset to 0x0005 ( workbook globals)
Window1
FONT - At least five of these records must be included
XF - At least 1 5 Style XF records and 1 Cell XF record must be included
STYLE
BOUNDSHEET - Include one BOUNDSHEET record per worksheet
EOF
BOF - Set the 6 byte offset to 0x0010 ( worksheet)
INDEX
DIMENSIONS
WINDOW2
EOF
2009-04-21 15:08:43 +00:00
The row and column numbering in BIFF files is zero- based.
2008-02-24 13:18:34 +00:00
Excel file format specification obtained from:
http: //sc.openoffice.org/excelfileformat.pdf
2009-04-21 15:08:43 +00:00
AUTHORS: Felipe Monteiro de Carvalho
Jose Mejuto
2008-02-24 13:18:34 +00:00
}
unit xlsbiff8;
{$ifdef fpc}
2009-04-21 15:08:43 +00:00
{$mode delphi}
2008-02-24 13:18:34 +00:00
{$endif}
2010-07-29 20:59:04 +00:00
// The new OLE code is much better, so always use it
{$define USE_NEW_OLE}
2014-04-08 09:53:02 +00:00
{.$define FPSPREADDEBUG} //used to be XLSDEBUG
2010-07-29 20:59:04 +00:00
2008-02-24 13:18:34 +00:00
interface
uses
2012-01-23 13:48:39 +00:00
Classes, SysUtils, fpcanvas, DateUtils,
2011-05-29 17:21:51 +00:00
fpspreadsheet, xlscommon,
2009-05-26 18:32:09 +00:00
{$ifdef USE_NEW_OLE}
fpolebasic,
{$else}
fpolestorage,
{$endif}
2011-12-24 23:38:24 +00:00
fpsutils, lazutf8;
2008-02-24 13:18:34 +00:00
type
2012-01-23 13:24:13 +00:00
TXFRecordData = class
public
FormatIndex: Integer ;
end ;
TFormatRecordData = class
public
Index : Integer ;
FormatString: widestring ;
end ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ TsSpreadBIFF8Reader }
2011-05-26 09:25:56 +00:00
TsSpreadBIFF8Reader = class( TsSpreadBIFFReader)
2009-04-21 15:08:43 +00:00
private
RecordSize: Word ;
2009-09-01 23:00:13 +00:00
PendingRecordSize: SizeInt;
2009-04-21 15:08:43 +00:00
FWorksheet: TsWorksheet;
FWorksheetNames: TStringList;
FCurrentWorksheet: Integer ;
2009-09-01 23:00:13 +00:00
FSharedStringTable: TStringList;
2012-01-23 13:24:13 +00:00
FXFList: TFPList; // of TXFRecordData
FFormatList: TFPList; // of TFormatRecordData
2009-09-01 23:00:13 +00:00
function DecodeRKValue( const ARK: DWORD) : Double ;
2013-12-22 14:02:04 +00:00
procedure ExtractNumberFormat( AXFIndex: WORD ;
out ANumberFormat: TsNumberFormat; out ADecimals: Word ;
out ANumberFormatStr: String ) ;
2013-12-07 13:42:22 +00:00
// Tries to find if a number cell is actually a date/datetime/time cell
// and retrieve the value
2013-12-22 14:02:04 +00:00
function IsDateTime( Number: Double ; ANumberFormat: TsNumberFormat; var ADateTime: TDateTime) : Boolean ;
2013-12-07 13:42:22 +00:00
function ReadWideString( const AStream: TStream; const ALength: WORD ) : WideString ; overload ;
2012-01-23 13:24:13 +00:00
function ReadWideString( const AStream: TStream; const AUse8BitLength: Boolean ) : WideString ; overload ;
2009-04-21 15:08:43 +00:00
procedure ReadWorkbookGlobals( AStream: TStream; AData: TsWorkbook) ;
procedure ReadWorksheet( AStream: TStream; AData: TsWorkbook) ;
procedure ReadBoundsheet( AStream: TStream) ;
2009-09-01 23:00:13 +00:00
procedure ReadRKValue( const AStream: TStream) ;
procedure ReadMulRKValues( const AStream: TStream) ;
procedure ReadRowColXF( const AStream: TStream; out ARow, ACol, AXF: WORD ) ;
function ReadString( const AStream: TStream; const ALength: WORD ) : UTF8String ;
procedure ReadRichString( const AStream: TStream) ;
procedure ReadSST( const AStream: TStream) ;
procedure ReadLabelSST( const AStream: TStream) ;
2013-12-07 13:42:22 +00:00
// Read XF record
2012-01-23 13:24:13 +00:00
procedure ReadXF( const AStream: TStream) ;
2013-12-07 13:42:22 +00:00
// Read FORMAT record (cell formatting)
2012-01-23 13:24:13 +00:00
procedure ReadFormat( const AStream: TStream) ;
2013-12-07 13:42:22 +00:00
// Finds format record for XF record pointed to by cell
// Will not return info for built-in formats
function FindFormatRecordForCell( const AXFIndex: Integer ) : TFormatRecordData;
2011-12-25 09:03:20 +00:00
// Workbook Globals records
// procedure ReadCodepage in xlscommon
2013-12-07 13:42:22 +00:00
// procedure ReadDateMode in xlscommon
2011-12-24 23:38:24 +00:00
procedure ReadFont( const AStream: TStream) ;
2009-04-21 15:08:43 +00:00
public
2012-01-23 13:24:13 +00:00
constructor Create; override ;
destructor Destroy; override ;
2009-04-21 15:08:43 +00:00
{ General reading methods }
procedure ReadFromFile( AFileName: string ; AData: TsWorkbook) ; override ;
procedure ReadFromStream( AStream: TStream; AData: TsWorkbook) ; override ;
{ Record writing methods }
procedure ReadFormula( AStream: TStream) ; override ;
procedure ReadLabel( AStream: TStream) ; override ;
procedure ReadNumber( AStream: TStream) ; override ;
end ;
{ TsSpreadBIFF8Writer }
2008-02-24 13:18:34 +00:00
2011-05-26 09:25:56 +00:00
TsSpreadBIFF8Writer = class( TsSpreadBIFFWriter)
2011-05-25 15:50:18 +00:00
private
2013-12-27 18:01:59 +00:00
// Convert our representation of RGB color to physical ARGB in Excel file
function LongRGBToExcelPhysical( const RGB: DWord) : DWord;
2013-12-07 13:42:22 +00:00
// Writes index to XF record according to cell's formatting
2011-05-25 15:50:18 +00:00
procedure WriteXFIndex( AStream: TStream; ACell: PCell) ;
2011-05-26 09:25:56 +00:00
procedure WriteXFFieldsForFormattingStyles( AStream: TStream) ;
protected
procedure AddDefaultFormats( ) ; override ;
2008-02-24 13:18:34 +00:00
public
2009-04-21 15:08:43 +00:00
// constructor Create;
// destructor Destroy; override;
2008-02-24 13:18:34 +00:00
{ General writing methods }
2009-11-08 19:21:23 +00:00
procedure WriteToFile( const AFileName: string ; AData: TsWorkbook;
const AOverwriteExisting: Boolean = False ) ; override ;
2008-02-24 13:18:34 +00:00
procedure WriteToStream( AStream: TStream; AData: TsWorkbook) ; override ;
{ Record writing methods }
2009-04-21 15:08:43 +00:00
procedure WriteBOF( AStream: TStream; ADataType: Word ) ;
function WriteBoundsheet( AStream: TStream; ASheetName: string ) : Int64 ;
2013-12-07 13:42:22 +00:00
// procedure WriteCodepage in xlscommon; Workbook Globals record
procedure WriteDateTime( AStream: TStream; const ARow, ACol: Cardinal ; const AValue: TDateTime; ACell: PCell) ; override ;
// procedure WriteDateMode in xlscommon; Workbook Globals record
2011-05-29 17:21:51 +00:00
procedure WriteDimensions( AStream: TStream; AWorksheet: TsWorksheet) ;
2008-02-24 13:18:34 +00:00
procedure WriteEOF( AStream: TStream) ;
2010-07-30 06:44:53 +00:00
procedure WriteFont( AStream: TStream; AFont: TFPCustomFont) ;
2013-12-07 13:42:22 +00:00
procedure WriteFormula( AStream: TStream; const ARow, ACol: Cardinal ; const AFormula: TsFormula; ACell: PCell) ; override ;
2009-04-21 15:08:43 +00:00
procedure WriteIndex( AStream: TStream) ;
2013-12-07 13:42:22 +00:00
procedure WriteLabel( AStream: TStream; const ARow, ACol: Cardinal ; const AValue: string ; ACell: PCell) ; override ;
2010-12-08 10:24:15 +00:00
procedure WriteNumber( AStream: TStream; const ARow, ACol: Cardinal ; const AValue: double ; ACell: PCell) ; override ;
2011-05-26 11:04:30 +00:00
procedure WritePalette( AStream: TStream) ;
2013-12-07 13:42:22 +00:00
procedure WriteRPNFormula( AStream: TStream; const ARow, ACol: Cardinal ; const AFormula: TsRPNFormula; ACell: PCell) ; override ;
2009-04-21 15:08:43 +00:00
procedure WriteStyle( AStream: TStream) ;
procedure WriteWindow1( AStream: TStream) ;
procedure WriteWindow2( AStream: TStream; ASheetSelected: Boolean ) ;
2010-07-30 06:44:53 +00:00
procedure WriteXF( AStream: TStream; AFontIndex: Word ;
2013-12-07 13:42:22 +00:00
AFormatIndex: Word ; AXF_TYPE_PROT, ATextRotation: Byte ; ABorders: TsCellBorders;
2011-05-26 10:00:56 +00:00
AddBackground: Boolean = False ; ABackgroundColor: TsColor = scSilver) ;
2008-02-24 13:18:34 +00:00
end ;
implementation
const
{ Excel record IDs }
INT_EXCEL_ID_BOF = $0809 ;
2009-08-12 13:08:44 +00:00
INT_EXCEL_ID_BOUNDSHEET = $0085 ; // Renamed to SHEET in the latest OpenOffice docs
2014-04-08 09:48:30 +00:00
INT_EXCEL_ID_COUNTRY = $008C ;
2008-02-24 13:18:34 +00:00
INT_EXCEL_ID_EOF = $000A ;
2009-04-21 15:08:43 +00:00
INT_EXCEL_ID_DIMENSIONS = $0200 ;
2008-02-24 13:18:34 +00:00
INT_EXCEL_ID_FONT = $0031 ;
INT_EXCEL_ID_FORMULA = $0006 ;
2009-04-21 15:08:43 +00:00
INT_EXCEL_ID_INDEX = $020B ;
INT_EXCEL_ID_LABEL = $0204 ;
2008-02-24 13:18:34 +00:00
INT_EXCEL_ID_NUMBER = $0203 ;
2009-04-21 15:08:43 +00:00
INT_EXCEL_ID_STYLE = $0293 ;
INT_EXCEL_ID_WINDOW1 = $003D ;
INT_EXCEL_ID_WINDOW2 = $023E ;
2008-02-24 13:18:34 +00:00
INT_EXCEL_ID_XF = $00E0 ;
2009-09-01 23:00:13 +00:00
INT_EXCEL_ID_RSTRING = $00D6 ;
INT_EXCEL_ID_RK = $027E ;
2009-09-04 22:18:43 +00:00
INT_EXCEL_ID_MULRK = $00BD ;
INT_EXCEL_ID_SST = $00FC ; //BIFF8 only
2009-09-01 23:00:13 +00:00
INT_EXCEL_ID_CONTINUE = $003C ;
INT_EXCEL_ID_LABELSST = $00FD ; //BIFF8 only
2011-05-26 11:04:30 +00:00
INT_EXCEL_ID_PALETTE = $0092 ;
2011-12-24 23:38:24 +00:00
INT_EXCEL_ID_CODEPAGE = $0042 ;
2012-01-23 13:24:13 +00:00
INT_EXCEL_ID_FORMAT = $041E ;
2014-04-08 09:48:30 +00:00
INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3 ;
2008-02-24 13:18:34 +00:00
{ Cell Addresses constants }
MASK_EXCEL_ROW = $3FFF ;
2013-02-12 12:13:49 +00:00
MASK_EXCEL_COL_BITS_BIFF8= $00FF ;
2014-04-08 09:48:30 +00:00
MASK_EXCEL_RELATIVE_COL = $4000 ; // This is according to Microsoft documentation,
MASK_EXCEL_RELATIVE_ROW = $8000 ; // but opposite to OpenOffice documentation!
2008-02-24 13:18:34 +00:00
{ BOF record constants }
2009-04-21 15:08:43 +00:00
INT_BOF_BIFF8_VER = $0600 ;
INT_BOF_WORKBOOK_GLOBALS= $0005 ;
INT_BOF_VB_MODULE = $0006 ;
INT_BOF_SHEET = $0010 ;
INT_BOF_CHART = $0020 ;
INT_BOF_MACRO_SHEET = $0040 ;
INT_BOF_WORKSPACE = $0100 ;
INT_BOF_BUILD_ID = $1FD2 ;
INT_BOF_BUILD_YEAR = $07CD ;
{ FONT record constants }
INT_FONT_WEIGHT_NORMAL = $0190 ;
2010-12-07 12:59:17 +00:00
INT_FONT_WEIGHT_BOLD = $02BC ;
2009-04-21 15:08:43 +00:00
{ FORMULA record constants }
MASK_FORMULA_RECALCULATE_ALWAYS = $0001 ;
MASK_FORMULA_RECALCULATE_ON_OPEN = $0002 ;
MASK_FORMULA_SHARED_FORMULA = $0008 ;
{ STYLE record constants }
MASK_STYLE_BUILT_IN = $8000 ;
{ WINDOW1 record constants }
MASK_WINDOW1_OPTION_WINDOW_HIDDEN = $0001 ;
MASK_WINDOW1_OPTION_WINDOW_MINIMISED = $0002 ;
MASK_WINDOW1_OPTION_HORZ_SCROLL_VISIBLE = $0008 ;
MASK_WINDOW1_OPTION_VERT_SCROLL_VISIBLE = $0010 ;
MASK_WINDOW1_OPTION_WORKSHEET_TAB_VISIBLE = $0020 ;
{ WINDOW2 record constants }
MASK_WINDOW2_OPTION_SHOW_FORMULAS = $0001 ;
MASK_WINDOW2_OPTION_SHOW_GRID_LINES = $0002 ;
MASK_WINDOW2_OPTION_SHOW_SHEET_HEADERS = $0004 ;
MASK_WINDOW2_OPTION_PANES_ARE_FROZEN = $0008 ;
MASK_WINDOW2_OPTION_SHOW_ZERO_VALUES = $0010 ;
MASK_WINDOW2_OPTION_AUTO_GRIDLINE_COLOR = $0020 ;
MASK_WINDOW2_OPTION_COLUMNS_RIGHT_TO_LEFT = $0040 ;
MASK_WINDOW2_OPTION_SHOW_OUTLINE_SYMBOLS = $0080 ;
MASK_WINDOW2_OPTION_REMOVE_SPLITS_ON_UNFREEZE = $0100 ;
MASK_WINDOW2_OPTION_SHEET_SELECTED = $0200 ;
MASK_WINDOW2_OPTION_SHEET_ACTIVE = $0400 ;
{ XF substructures }
{ XF_TYPE_PROT - XF Type and Cell protection (3 Bits) - BIFF3-BIFF8 }
MASK_XF_TYPE_PROT_LOCKED = $1 ;
MASK_XF_TYPE_PROT_FORMULA_HIDDEN = $2 ;
MASK_XF_TYPE_PROT_STYLE_XF = $4 ; // 0 = CELL XF
{ XF_USED_ATTRIB - Attributes from parent Style XF ( 6 Bits) - BIFF3- BIFF8
In a CELL XF a cleared bit means that the parent attribute is used,
while a set bit indicates that the data in this XF is used
In a STYLE XF a cleared bit means that the data in this XF is used,
while a set bit indicates that the attribute should be ignored }
MASK_XF_USED_ATTRIB_NUMBER_FORMAT = $04 ;
MASK_XF_USED_ATTRIB_FONT = $08 ;
MASK_XF_USED_ATTRIB_TEXT = $10 ;
MASK_XF_USED_ATTRIB_BORDER_LINES = $20 ;
MASK_XF_USED_ATTRIB_BACKGROUND = $40 ;
MASK_XF_USED_ATTRIB_CELL_PROTECTION = $80 ;
{ XF_VERT_ALIGN }
MASK_XF_VERT_ALIGN_TOP = $00 ;
MASK_XF_VERT_ALIGN_CENTRED = $10 ;
MASK_XF_VERT_ALIGN_BOTTOM = $20 ;
MASK_XF_VERT_ALIGN_JUSTIFIED = $30 ;
2008-02-24 13:18:34 +00:00
2010-07-30 06:44:53 +00:00
{ XF_ROTATION }
XF_ROTATION_HORIZONTAL = 0 ;
XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE = 9 0 ;
XF_ROTATION_90_DEGREE_CLOCKWISE = 1 8 0 ;
2008-02-24 13:18:34 +00:00
{ XF record constants }
2009-04-21 15:08:43 +00:00
MASK_XF_TYPE_PROT = $0007 ;
MASK_XF_TYPE_PROT_PARENT = $FFF0 ;
MASK_XF_VERT_ALIGN = $70 ;
2008-02-24 13:18:34 +00:00
{
2009-04-21 15:08:43 +00:00
Exported functions
2008-02-24 13:18:34 +00:00
}
2009-04-21 15:08:43 +00:00
{ TsSpreadBIFF8Writer }
2013-12-27 18:01:59 +00:00
function TsSpreadBIFF8Writer. LongRGBToExcelPhysical( const RGB: DWord) : DWord;
// Converts RGB part of a LongRGB logical structure
// to its physical representation
// IOW: RGBA (where A is 0 and omitted in the function call) => ABGR
begin
{$IFDEF FPC}
{$IFDEF ENDIAN_LITTLE}
result : = ( RGB shl 8 ) ; //tags $00 at end for the A byte
2013-12-28 16:01:15 +00:00
result : = SwapEndian( result ) ; //flip byte order
2013-12-27 18:01:59 +00:00
{$ELSE}
//Big endian
result : = RGB; //leave value as is //todo: verify if this turns out ok
{$ENDIF}
{$ELSE}
// messed up result
{$ENDIF}
end ;
2011-05-26 09:25:56 +00:00
{ Index to XF record, according to formatting }
2011-05-25 15:50:18 +00:00
procedure TsSpreadBIFF8Writer. WriteXFIndex( AStream: TStream; ACell: PCell) ;
2011-05-26 09:25:56 +00:00
var
lIndex: Integer ;
lXFIndex: Word ;
2011-05-25 15:50:18 +00:00
begin
2011-05-26 09:25:56 +00:00
// First try the fast methods for default formats
if ACell^ . UsedFormattingFields = [ ] then
begin
2013-12-07 13:42:22 +00:00
AStream. WriteWord( WordToLE( 1 5 ) ) ; //XF15; see TsSpreadBIFF8Writer.AddDefaultFormats
2011-05-26 09:25:56 +00:00
Exit;
end ;
2011-05-25 15:50:18 +00:00
if ACell^ . UsedFormattingFields = [ uffTextRotation] then
begin
case ACell^ . TextRotation of
2013-12-07 13:42:22 +00:00
rt90DegreeCounterClockwiseRotation: AStream. WriteWord( WordToLE( 1 6 ) ) ; //XF_16
rt90DegreeClockwiseRotation: AStream. WriteWord( WordToLE( 1 7 ) ) ; //XF_17
else
AStream. WriteWord( WordToLE( 1 5 ) ) ; //XF_15
end ;
Exit;
end ;
{
uffNumberFormat does not seem to have default XF indexes, but perhaps look at XF_21
if ACell^ . UsedFormattingFields = [ uffNumberFormat] then
begin
case ACell^ . NumberFormat of
nfShortDate: AStream. WriteWord( WordToLE( ? ? ? ) ) ; //what XF index?
nfShortDateTime: AStream. WriteWord( WordToLE( ? ? ? ) ) ; //what XF index?
2011-05-25 15:50:18 +00:00
else
2013-12-07 13:42:22 +00:00
AStream. WriteWord( WordToLE( 1 5 ) ) ; //e.g. nfGeneral: XF_15
2011-05-25 15:50:18 +00:00
end ;
2011-05-26 09:25:56 +00:00
Exit;
end ;
2013-12-07 13:42:22 +00:00
}
2011-05-26 09:25:56 +00:00
if ACell^ . UsedFormattingFields = [ uffBold] then
2011-05-25 15:50:18 +00:00
begin
2013-12-07 13:42:22 +00:00
AStream. WriteWord( WordToLE( 1 8 ) ) ; //XF_18
2011-05-26 09:25:56 +00:00
Exit;
end ;
2011-05-25 15:50:18 +00:00
2011-05-26 09:25:56 +00:00
// If not, then we need to search in the list of dynamic formats
lIndex : = FindFormattingInList( ACell) ;
// Carefully check the index
if ( lIndex < 0 ) or ( lIndex > Length( FFormattingStyles) ) then
raise Exception. Create( '[TsSpreadBIFF8Writer.WriteXFIndex] Invalid Index, this should not happen!' ) ;
lXFIndex : = FFormattingStyles[ lIndex] . Row;
AStream. WriteWord( WordToLE( lXFIndex) ) ;
2011-05-25 15:50:18 +00:00
end ;
2011-05-26 09:25:56 +00:00
procedure TsSpreadBIFF8Writer. WriteXFFieldsForFormattingStyles( AStream: TStream) ;
2011-05-25 15:50:18 +00:00
var
i: Integer ;
2011-05-26 09:25:56 +00:00
lFontIndex: Word ;
2013-12-07 13:42:22 +00:00
lFormatIndex: Word ; //number format
2011-05-26 09:25:56 +00:00
lTextRotation: Byte ;
lBorders: TsCellBorders;
2011-05-26 09:39:17 +00:00
lAddBackground: Boolean ;
2011-05-26 10:00:56 +00:00
lBackgroundColor: TsColor;
2013-12-22 14:02:04 +00:00
fmt: String ;
2011-05-25 15:50:18 +00:00
begin
2011-05-26 09:25:56 +00:00
// The first 4 styles were already added
for i : = 4 to Length( FFormattingStyles) - 1 do
2011-05-25 15:50:18 +00:00
begin
2011-05-26 09:25:56 +00:00
// Default styles
lFontIndex : = 0 ;
2013-12-07 13:42:22 +00:00
lFormatIndex : = 0 ; //General format (one of the built-in number formats)
2011-05-26 09:25:56 +00:00
lTextRotation : = XF_ROTATION_HORIZONTAL;
lBorders : = [ ] ;
2011-05-26 09:39:17 +00:00
lAddBackground : = False ;
2011-05-26 10:00:56 +00:00
lBackgroundColor : = FFormattingStyles[ i] . BackgroundColor;
2011-05-26 09:25:56 +00:00
2013-12-07 13:42:22 +00:00
// Now apply the modifications.
if uffNumberFormat in FFormattingStyles[ i] . UsedFormattingFields then
case FFormattingStyles[ i] . NumberFormat of
2013-12-22 14:02:04 +00:00
nfFixed:
case FFormattingStyles[ i] . NumberDecimals of
0 : lFormatIndex : = FORMAT_FIXED_0_DECIMALS;
2 : lFormatIndex : = FORMAT_FIXED_2_DECIMALS;
end ;
nfFixedTh:
case FFormattingStyles[ i] . NumberDecimals of
0 : lFormatIndex : = FORMAT_FIXED_THOUSANDS_0_DECIMALS;
2 : lFormatIndex : = FORMAT_FIXED_THOUSANDS_2_DECIMALS;
end ;
nfExp:
lFormatIndex : = FORMAT_EXP_2_DECIMALS;
nfSci:
lFormatIndex : = FORMAT_SCI_1_DECIMAL;
nfPercentage:
case FFormattingStyles[ i] . NumberDecimals of
0 : lFormatIndex : = FORMAT_PERCENT_0_DECIMALS;
2 : lFormatIndex : = FORMAT_PERCENT_2_DECIMALS;
end ;
{
nfCurrency:
case FFormattingStyles[ i] . NumberDecimals of
0 : lFormatIndex : = FORMAT_CURRENCY_0_DECIMALS;
2 : lFormatIndex : = FORMAT_CURRENCY_2_DECIMALS;
end ;
}
nfShortDate:
lFormatIndex : = FORMAT_SHORT_DATE;
nfShortTime:
lFormatIndex : = FORMAT_SHORT_TIME;
nfLongTime:
lFormatIndex : = FORMAT_LONG_TIME;
nfShortTimeAM:
lFormatIndex : = FORMAT_SHORT_TIME_AM;
nfLongTimeAM:
lFormatIndex : = FORMAT_LONG_TIME_AM;
nfShortDateTime:
lFormatIndex : = FORMAT_SHORT_DATETIME;
nfFmtDateTime:
begin
fmt : = lowercase( FFormattingStyles[ i] . NumberFormatStr) ;
if ( fmt = 'dm' ) or ( fmt = 'd-mmm' ) or ( fmt = 'd mmm' ) or ( fmt = 'd. mmm' ) or ( fmt = 'd/mmm' ) then
lFormatIndex : = FORMAT_DATE_DM
else
if ( fmt = 'my' ) or ( fmt = 'mmm-yy' ) or ( fmt = 'mmm yy' ) or ( fmt = 'mmm/yy' ) then
lFormatIndex : = FORMAT_DATE_MY
else
if ( fmt = 'ms' ) or ( fmt = 'nn:ss' ) or ( fmt = 'mm:ss' ) then
lFormatIndex : = FORMAT_TIME_MS
else
if ( fmt = 'msz' ) or ( fmt = 'nn:ss.zzz' ) or ( fmt = 'mm:ss.zzz' ) or ( fmt = 'mm:ss.0' ) or ( fmt = 'mm:ss.z' ) or ( fmt = 'nn:ss.z' ) then
lFormatIndex : = FORMAT_TIME_MSZ
end ;
nfTimeInterval:
lFormatIndex : = FORMAT_TIME_INTERVAL;
2013-12-07 13:42:22 +00:00
end ;
2011-05-26 09:25:56 +00:00
if uffBorder in FFormattingStyles[ i] . UsedFormattingFields then
lBorders : = FFormattingStyles[ i] . Border;
if uffTextRotation in FFormattingStyles[ i] . UsedFormattingFields then
begin
case FFormattingStyles[ i] . TextRotation of
trHorizontal: lTextRotation : = XF_ROTATION_HORIZONTAL;
rt90DegreeClockwiseRotation: lTextRotation : = XF_ROTATION_90_DEGREE_CLOCKWISE;
rt90DegreeCounterClockwiseRotation: lTextRotation : = XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE;
end ;
end ;
if uffBold in FFormattingStyles[ i] . UsedFormattingFields then
lFontIndex : = 1 ;
2011-05-25 15:50:18 +00:00
2011-05-26 09:39:17 +00:00
if uffBackgroundColor in FFormattingStyles[ i] . UsedFormattingFields then
lAddBackground : = True ;
2011-05-26 09:25:56 +00:00
// And finally write the style
2013-12-07 13:42:22 +00:00
WriteXF( AStream, lFontIndex, lFormatIndex, 0 , lTextRotation, lBorders, lAddBackground, lBackgroundColor) ;
2011-05-26 09:25:56 +00:00
end ;
2011-05-25 15:50:18 +00:00
end ;
2011-05-26 09:25:56 +00:00
{ @ @
2013-12-07 13:42:22 +00:00
These are default style formats which are added as XF fields regardless of being used
2011-05-26 09:25:56 +00:00
in the document or not .
}
procedure TsSpreadBIFF8Writer. AddDefaultFormats( ) ;
2011-05-25 15:50:18 +00:00
begin
2013-12-07 13:42:22 +00:00
NextXFIndex : = 2 1 ;
2011-05-26 09:25:56 +00:00
2013-12-07 13:42:22 +00:00
SetLength( FFormattingStyles, 6 ) ;
2011-05-26 09:25:56 +00:00
2013-12-07 13:42:22 +00:00
// XF0..XF14: Normal style, Row Outline level 1..7,
// Column Outline level 1..7.
// XF15 - Default cell format, no formatting (4.6.2)
2011-05-26 09:25:56 +00:00
FFormattingStyles[ 0 ] . UsedFormattingFields : = [ ] ;
FFormattingStyles[ 0 ] . Row : = 1 5 ;
// XF16 - Rotated
FFormattingStyles[ 1 ] . UsedFormattingFields : = [ uffTextRotation] ;
FFormattingStyles[ 1 ] . Row : = 1 6 ;
FFormattingStyles[ 1 ] . TextRotation : = rt90DegreeCounterClockwiseRotation;
// XF17 - Rotated
FFormattingStyles[ 2 ] . UsedFormattingFields : = [ uffTextRotation] ;
FFormattingStyles[ 2 ] . Row : = 1 7 ;
FFormattingStyles[ 2 ] . TextRotation : = rt90DegreeClockwiseRotation;
// XF18 - Bold
FFormattingStyles[ 3 ] . UsedFormattingFields : = [ uffBold] ;
FFormattingStyles[ 3 ] . Row : = 1 8 ;
2011-05-25 15:50:18 +00:00
end ;
2009-04-21 15:08:43 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteToFile ( )
*
* DESCRIPTION: Writes an Excel BIFF8 file to the disc
*
* The BIFF 8 writer overrides this method because
* BIFF 8 is written as an OLE document, and our
* current OLE document writing method involves:
*
* 1 - Writing the BIFF data to a memory stream
*
* 2 - Write the memory stream data to disk using
* COM functions
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
2009-11-08 19:21:23 +00:00
procedure TsSpreadBIFF8Writer. WriteToFile( const AFileName: string ;
AData: TsWorkbook; const AOverwriteExisting: Boolean ) ;
2009-04-21 15:08:43 +00:00
var
MemStream: TMemoryStream;
OutputStorage: TOLEStorage;
OLEDocument: TOLEDocument;
2008-02-24 13:18:34 +00:00
begin
2009-04-21 15:08:43 +00:00
MemStream : = TMemoryStream. Create;
OutputStorage : = TOLEStorage. Create;
try
WriteToStream( MemStream, AData) ;
// Only one stream is necessary for any number of worksheets
OLEDocument. Stream : = MemStream;
2009-11-08 19:21:23 +00:00
OutputStorage. WriteOLEFile( AFileName, OLEDocument, AOverwriteExisting, 'Workbook' ) ;
2009-04-21 15:08:43 +00:00
finally
MemStream. Free;
OutputStorage. Free;
end ;
2008-02-24 13:18:34 +00:00
end ;
2009-04-21 15:08:43 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteToStream ( )
*
* DESCRIPTION: Writes an Excel BIFF8 record structure
*
* Be careful as this method doesn' t write the OLE
* part of the document, just the BIFF records
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteToStream( AStream: TStream; AData: TsWorkbook) ;
var
FontData: TFPCustomFont;
MyData: TMemoryStream;
CurrentPos: Int64 ;
Boundsheets: array of Int64 ;
i, len: Integer ;
begin
{ Write workbook globals }
WriteBOF( AStream, INT_BOF_WORKBOOK_GLOBALS) ;
WriteWindow1( AStream) ;
FontData : = TFPCustomFont. Create;
try
FontData. Name : = 'Arial' ;
2010-12-07 12:59:17 +00:00
// FONT0 - normal
2009-04-21 15:08:43 +00:00
WriteFont( AStream, FontData) ;
2010-12-07 12:59:17 +00:00
// FONT1 - bold
FontData. Bold : = True ;
2009-04-21 15:08:43 +00:00
WriteFont( AStream, FontData) ;
2010-12-07 12:59:17 +00:00
FontData. Bold : = False ;
2009-04-21 15:08:43 +00:00
// FONT2
WriteFont( AStream, FontData) ;
// FONT3
WriteFont( AStream, FontData) ;
// FONT5
WriteFont( AStream, FontData) ;
finally
FontData. Free;
end ;
2011-05-26 11:04:30 +00:00
// PALETTE
WritePalette( AStream) ;
2009-04-21 15:08:43 +00:00
// XF0
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF1
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF2
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF3
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF4
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF5
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF6
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF7
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF8
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF9
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF10
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF11
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF12
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF13
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2009-04-21 15:08:43 +00:00
// XF14
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [ ] ) ;
2011-05-25 13:40:53 +00:00
// XF15 - Default, no formatting
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , 0 , XF_ROTATION_HORIZONTAL, [ ] ) ;
2011-05-25 13:40:53 +00:00
// XF16 - Rotated
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , 0 , XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE, [ ] ) ;
2011-05-25 13:40:53 +00:00
// XF17 - Rotated
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 0 , 0 , 0 , XF_ROTATION_90_DEGREE_CLOCKWISE, [ ] ) ;
2011-05-25 13:40:53 +00:00
// XF18 - Bold
2013-12-07 13:42:22 +00:00
WriteXF( AStream, 1 , 0 , 0 , XF_ROTATION_HORIZONTAL, [ ] ) ;
// Add all further non-standard/built-in formatting styles
2011-05-26 09:25:56 +00:00
ListAllFormattingStyles( AData) ;
WriteXFFieldsForFormattingStyles( AStream) ;
2009-04-21 15:08:43 +00:00
WriteStyle( AStream) ;
// A BOUNDSHEET for each worksheet
for i : = 0 to AData. GetWorksheetCount - 1 do
begin
len : = Length( Boundsheets) ;
SetLength( Boundsheets, len + 1 ) ;
Boundsheets[ len] : = WriteBoundsheet( AStream, AData. GetWorksheetByIndex( i) . Name ) ;
end ;
2014-04-08 09:48:30 +00:00
2009-04-21 15:08:43 +00:00
WriteEOF( AStream) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ Write each worksheet }
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
for i : = 0 to AData. GetWorksheetCount - 1 do
begin
{ First goes back and writes the position of the BOF of the
sheet on the respective BOUNDSHEET record }
CurrentPos : = AStream. Position;
AStream. Position : = Boundsheets[ i] ;
AStream. WriteDWord( DWordToLE( DWORD( CurrentPos) ) ) ;
AStream. Position : = CurrentPos;
WriteBOF( AStream, INT_BOF_SHEET) ;
WriteIndex( AStream) ;
2011-05-29 17:21:51 +00:00
WriteDimensions( AStream, AData. GetWorksheetByIndex( i) ) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
WriteWindow2( AStream, True ) ;
2009-09-02 22:03:01 +00:00
WriteCellsToStream( AStream, AData. GetWorksheetByIndex( i) . Cells) ;
2009-04-21 15:08:43 +00:00
WriteEOF( AStream) ;
end ;
{ Cleanup }
SetLength( Boundsheets, 0 ) ;
2008-02-24 13:18:34 +00:00
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2009-04-21 15:08:43 +00:00
* TsSpreadBIFF8Writer. WriteBOF ( )
2008-02-24 13:18:34 +00:00
*
2009-04-21 15:08:43 +00:00
* DESCRIPTION: Writes an Excel 8 BOF record
2008-02-24 13:18:34 +00:00
*
2009-04-21 15:08:43 +00:00
* This must be the first record on an Excel 8 stream
2008-02-24 13:18:34 +00:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Writer. WriteBOF( AStream: TStream; ADataType: Word ) ;
2008-02-24 13:18:34 +00:00
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_BOF) ) ;
2013-12-07 13:42:22 +00:00
AStream. WriteWord( WordToLE( 1 6 ) ) ; //total record size
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ BIFF version. Should only be used if this BOF is for the workbook globals }
{ OpenOffice rejects to correctly read xls files if this field is
omitted as docs. says, or even if it is being written to zero value,
Not tested with Excel, but MSExcel reader opens it as expected }
AStream. WriteWord( WordToLE( INT_BOF_BIFF8_VER) ) ;
2008-02-24 13:18:34 +00:00
{ Data type }
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( ADataType) ) ;
2008-02-24 13:18:34 +00:00
{ Build identifier, must not be 0 }
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( INT_BOF_BUILD_ID) ) ;
2008-02-24 13:18:34 +00:00
{ Build year, must not be 0 }
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( INT_BOF_BUILD_YEAR) ) ;
2008-02-24 13:18:34 +00:00
{ File history flags }
2009-04-21 15:08:43 +00:00
AStream. WriteDWord( DWordToLE( 0 ) ) ;
{ Lowest Excel version that can read all records in this file 5?}
AStream. WriteDWord( DWordToLE( 0 ) ) ; //?????????
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteBoundsheet ( )
*
* DESCRIPTION: Writes an Excel 8 BOUNDSHEET record
*
* Always located on the workbook globals substream.
*
* One BOUNDSHEET is written for each worksheet.
*
* RETURNS: The stream position where the absolute stream position
* of the BOF of this sheet should be written ( 4 bytes size) .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
function TsSpreadBIFF8Writer. WriteBoundsheet( AStream: TStream; ASheetName: string ) : Int64 ;
var
Len: Byte ;
WideSheetName: WideString ;
begin
WideSheetName: = UTF8Decode( ASheetName) ;
Len : = Length( WideSheetName) ;
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_BOUNDSHEET) ) ;
AStream. WriteWord( WordToLE( 6 + 1 + 1 + Len * Sizeof( WideChar ) ) ) ;
{ Absolute stream position of the BOF record of the sheet represented
by this record }
Result : = AStream. Position;
AStream. WriteDWord( DWordToLE( 0 ) ) ;
{ Visibility }
AStream. WriteByte( 0 ) ;
{ Sheet type }
AStream. WriteByte( 0 ) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ Sheet name: Unicode string char count 1 byte }
AStream. WriteByte( Len) ;
{String flags}
AStream. WriteByte( 1 ) ;
AStream. WriteBuffer( WideStringToLE( WideSheetName) [ 1 ] , Len * Sizeof( WideChar ) ) ;
2008-02-24 13:18:34 +00:00
end ;
2013-12-07 13:42:22 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteDateTime ( )
*
* DESCRIPTION: Writes a date/ time/ datetime to an
* Excel 8 NUMBER record , with a date/ time format
* ( There is no separate date record type in xls)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteDateTime( AStream: TStream; const ARow,
ACol: Cardinal ; const AValue: TDateTime; ACell: PCell) ;
var
ExcelDateSerial: double ;
begin
ExcelDateSerial: = ConvertDateTimeToExcelDateTime( AValue, FDateMode) ;
// fpspreadsheet must already have set formatting to a date/datetime format, so
// this will get written out as a pointer to the relevant XF record.
// In the end, dates in xls are just numbers with a format. Pass it on to WriteNumber:
WriteNumber( AStream, ARow, ACol, ExcelDateSerial, ACell) ;
end ;
2011-05-30 07:03:56 +00:00
{
Writes an Excel 8 DIMENSIONS record
nm = ( rl - rf - 1 ) / 3 2 + 1 ( using integer division)
Excel, OpenOffice and FPSpreadsheet ignore the dimensions written in this record ,
but some other applications really use them, so they need to be correct.
See bug 1 8 8 8 6 : excel5 files are truncated when imported
}
2011-05-29 17:21:51 +00:00
procedure TsSpreadBIFF8Writer. WriteDimensions( AStream: TStream; AWorksheet: TsWorksheet) ;
var
lLastCol: Word ;
lLastRow: Integer ;
2009-04-21 15:08:43 +00:00
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_DIMENSIONS) ) ;
AStream. WriteWord( WordToLE( 1 4 ) ) ;
{ Index to first used row }
AStream. WriteDWord( DWordToLE( 0 ) ) ;
{ Index to last used row, increased by 1 }
2011-05-29 17:21:51 +00:00
lLastRow : = GetLastRowIndex( AWorksheet) + 1 ;
AStream. WriteDWord( DWordToLE( lLastRow) ) ; // Old dummy value: 33
2009-04-21 15:08:43 +00:00
{ Index to first used column }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Index to last used column, increased by 1 }
2011-05-29 17:21:51 +00:00
lLastCol : = GetLastColIndex( AWorksheet) + 1 ;
AStream. WriteWord( WordToLE( lLastCol) ) ; // Old dummy value: 10
2009-04-21 15:08:43 +00:00
{ Not used }
AStream. WriteWord( WordToLE( 0 ) ) ;
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteEOF ( )
*
* DESCRIPTION: Writes an Excel 8 EOF record
*
* This must be the last record on an Excel 8 stream
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteEOF( AStream: TStream) ;
2008-02-24 13:18:34 +00:00
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_EOF) ) ;
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( $0000 ) ) ;
2008-02-24 13:18:34 +00:00
end ;
2009-04-21 15:08:43 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteFont ( )
*
* DESCRIPTION: Writes an Excel 8 FONT record
*
* The font data is passed in an instance of TFPCustomFont
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteFont( AStream: TStream; AFont: TFPCustomFont) ;
2008-02-24 13:18:34 +00:00
var
Len: Byte ;
2009-04-21 15:08:43 +00:00
WideFontName: WideString ;
2008-02-24 13:18:34 +00:00
begin
2009-04-21 15:08:43 +00:00
WideFontName: = AFont. Name ;
Len : = Length( WideFontName) ;
2008-02-24 13:18:34 +00:00
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_FONT) ) ;
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( 1 4 + 1 + 1 + Len * Sizeof( WideChar ) ) ) ;
2008-02-24 13:18:34 +00:00
{ Height of the font in twips = 1/20 of a point }
AStream. WriteWord( WordToLE( 2 0 0 ) ) ;
{ Option flags }
2010-12-07 12:59:17 +00:00
if AFont. Bold then AStream. WriteWord( WordToLE( 1 ) )
else AStream. WriteWord( WordToLE( 0 ) ) ;
2008-02-24 13:18:34 +00:00
{ Colour index }
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( $7FFF ) ) ;
2008-02-24 13:18:34 +00:00
{ Font weight }
2010-12-07 12:59:17 +00:00
if AFont. Bold then AStream. WriteWord( WordToLE( INT_FONT_WEIGHT_BOLD) )
else AStream. WriteWord( WordToLE( INT_FONT_WEIGHT_NORMAL) ) ;
2009-04-21 15:08:43 +00:00
{ Escapement type }
AStream. WriteWord( WordToLE( 0 ) ) ;
2008-02-24 13:18:34 +00:00
{ Underline type }
AStream. WriteByte( 0 ) ;
{ Font family }
AStream. WriteByte( 0 ) ;
{ Character set }
AStream. WriteByte( 0 ) ;
{ Not used }
AStream. WriteByte( 0 ) ;
2009-04-21 15:08:43 +00:00
{ Font name: Unicodestring, char count in 1 byte }
2008-02-24 13:18:34 +00:00
AStream. WriteByte( Len) ;
2009-04-21 15:08:43 +00:00
{ Widestring flags, 1=regular unicode LE string }
AStream. WriteByte( 1 ) ;
AStream. WriteBuffer( WideStringToLE( WideFontName) [ 1 ] , Len * Sizeof( WideChar ) ) ;
2008-02-24 13:18:34 +00:00
end ;
2014-04-08 09:48:30 +00:00
2008-02-24 13:18:34 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2009-04-21 15:08:43 +00:00
* TsSpreadBIFF8Writer. WriteFormula ( )
2008-02-24 13:18:34 +00:00
*
* DESCRIPTION: Writes an Excel 5 FORMULA record
*
* To input a formula to this method, first convert it
* to RPN, and then list all it' s members in the
* AFormula array
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Writer. WriteFormula( AStream: TStream; const ARow,
2013-12-07 13:42:22 +00:00
ACol: Cardinal ; const AFormula: TsFormula; ACell: PCell) ;
2009-02-02 09:58:51 +00:00
{ var
2008-02-24 13:18:34 +00:00
FormulaResult: double ;
i: Integer ;
RPNLength: Word ;
2009-04-21 15:08:43 +00:00
TokenArraySizePos, RecordSizePos, FinalPos: Int64 ; }
2008-02-24 13:18:34 +00:00
begin
2009-02-02 09:58:51 +00:00
( * RPNLength : = 0 ;
2008-02-24 13:18:34 +00:00
FormulaResult : = 0.0 ;
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_FORMULA) ) ;
RecordSizePos : = AStream. Position;
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( 2 2 + RPNLength) ) ;
2008-02-24 13:18:34 +00:00
{ BIFF Record data }
AStream. WriteWord( WordToLE( ARow) ) ;
AStream. WriteWord( WordToLE( ACol) ) ;
2009-04-21 15:08:43 +00:00
{ Index to XF Record }
AStream. WriteWord( $0000 ) ;
2008-02-24 13:18:34 +00:00
{ Result of the formula in IEE 754 floating-point value }
AStream. WriteBuffer( FormulaResult, 8 ) ;
2009-04-21 15:08:43 +00:00
{ Options flags }
AStream. WriteWord( WordToLE( MASK_FORMULA_RECALCULATE_ALWAYS) ) ;
{ Not used }
AStream. WriteDWord( 0 ) ;
2008-02-24 13:18:34 +00:00
{ 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;
2009-04-21 15:08:43 +00:00
AStream. WriteWord( RPNLength) ;
2008-02-24 13:18:34 +00:00
{ Formula data (RPN token array) }
for i : = 0 to Length( AFormula) - 1 do
begin
{ Token identifier }
AStream. WriteByte( AFormula[ i] . TokenID) ;
Inc( RPNLength) ;
{ Additional data }
case AFormula[ i] . 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;
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( 2 2 + RPNLength) ) ;
2009-02-02 09:58:51 +00:00
AStream. position : = FinalPos; * )
2008-02-24 13:18:34 +00:00
end ;
2011-05-30 09:22:08 +00:00
procedure TsSpreadBIFF8Writer. WriteRPNFormula( AStream: TStream; const ARow,
2013-12-07 13:42:22 +00:00
ACol: Cardinal ; const AFormula: TsRPNFormula; ACell: PCell) ;
2011-05-30 09:22:08 +00:00
var
FormulaResult: double ;
i: Integer ;
2014-04-08 09:48:30 +00:00
len: Integer ;
2011-05-30 09:22:08 +00:00
RPNLength: Word ;
TokenArraySizePos, RecordSizePos, FinalPos: Int64 ;
2014-04-08 09:48:30 +00:00
TokenID: Word ;
2013-02-12 12:13:49 +00:00
lSecondaryID: Word ;
2014-04-08 09:48:30 +00:00
c: Cardinal ;
wideStr: WideString ;
2011-05-30 09:22:08 +00:00
begin
RPNLength : = 0 ;
FormulaResult : = 0.0 ;
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_FORMULA) ) ;
RecordSizePos : = AStream. Position;
AStream. WriteWord( WordToLE( 2 2 + RPNLength) ) ;
{ BIFF Record data }
AStream. WriteWord( WordToLE( ARow) ) ;
AStream. WriteWord( WordToLE( ACol) ) ;
2013-02-12 11:29:19 +00:00
{ Index to XF record, according to formatting }
//AStream.WriteWord(0);
WriteXFIndex( AStream, ACell) ;
2011-05-30 09:22:08 +00:00
2014-04-08 09:48:30 +00:00
{ Result of the formula in IEEE 754 floating-point value }
2011-05-30 09:22:08 +00:00
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 }
2013-02-12 12:13:49 +00:00
TokenID : = FormulaElementKindToExcelTokenID( AFormula[ i] . ElementKind, lSecondaryID) ;
2011-05-30 09:22:08 +00:00
AStream. WriteByte( TokenID) ;
Inc( RPNLength) ;
{ Additional data }
case TokenID of
2011-05-30 10:03:04 +00:00
{ Operand Tokens }
2011-05-30 10:23:55 +00:00
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell }
begin
2013-02-12 12:13:49 +00:00
AStream. WriteWord( AFormula[ i] . Row) ;
2014-04-08 09:48:30 +00:00
c : = AFormula[ i] . Col and MASK_EXCEL_COL_BITS_BIFF8;
if ( rfRelRow in AFormula[ i] . RelFlags) then c : = c or MASK_EXCEL_RELATIVE_ROW;
if ( rfRelCol in AFormula[ i] . RelFlags) then c : = c or MASK_EXCEL_RELATIVE_COL;
AStream. WriteWord( c) ;
2013-02-12 12:13:49 +00:00
Inc( RPNLength, 4 ) ;
2011-05-30 10:23:55 +00:00
end ;
2011-05-30 09:22:08 +00:00
2011-05-30 10:03:04 +00:00
INT_EXCEL_TOKEN_TAREA_R: { fekCellRange }
begin
{
Cell range address, BIFF8:
Offset Size Contents
0 2 Index to first row ( 0 …6 5 5 3 5 ) or offset of first row ( method [ B] , - 3 2 7 6 8 …3 2 7 6 7 )
2 2 Index to last row ( 0 …6 5 5 3 5 ) or offset of last row ( method [ B] , - 3 2 7 6 8 …3 2 7 6 7 )
4 2 Index to first column or offset of first column, with relative flags ( see table above)
6 2 Index to last column or offset of last column, with relative flags ( see table above)
}
AStream. WriteWord( WordToLE( AFormula[ i] . Row) ) ;
AStream. WriteWord( WordToLE( AFormula[ i] . Row2) ) ;
2014-04-08 09:48:30 +00:00
c : = AFormula[ i] . Col;
if ( rfRelCol in AFormula[ i] . RelFlags) then c : = c or MASK_EXCEL_RELATIVE_COL;
if ( rfRelRow in AFormula[ i] . RelFlags) then c : = c or MASK_EXCEL_RELATIVE_ROW;
AStream. WriteWord( WordToLE( c) ) ;
c : = AFormula[ i] . Col2;
if ( rfRelCol2 in AFormula[ i] . RelFlags) then c : = c or MASK_EXCEL_RELATIVE_COL;
if ( rfRelRow2 in AFormula[ i] . RelFlags) then c : = c or MASK_EXCEL_RELATIVE_ROW;
AStream. WriteWord( WordToLE( c) ) ;
2011-05-30 10:03:04 +00:00
Inc( RPNLength, 8 ) ;
end ;
2011-05-30 09:22:08 +00:00
2011-05-30 10:03:04 +00:00
INT_EXCEL_TOKEN_TNUM: { fekNum }
2011-05-30 09:22:08 +00:00
begin
AStream. WriteBuffer( AFormula[ i] . DoubleValue, 8 ) ;
Inc( RPNLength, 8 ) ;
end ;
2014-04-08 09:48:30 +00:00
INT_EXCEL_TOKEN_TSTR: { fekString }
begin
// string constant is stored as widestring in BIFF8
wideStr : = AFormula[ i] . StringValue;
len : = Length( wideStr) ;
AStream. WriteByte( len) ; // char count in 1 byte
AStream. WriteByte( 1 ) ; // Widestring flags, 1=regular unicode LE string
AStream. WriteBuffer( WideStringToLE( wideStr) [ 1 ] , len * Sizeof( WideChar ) ) ;
Inc( RPNLength, 1 + 1 + len* SizeOf( WideChar ) ) ;
end ;
INT_EXCEL_TOKEN_TBOOL: { fekBool }
begin
AStream. WriteByte( ord( AFormula[ i] . DoubleValue < > 0.0 ) ) ;
inc( RPNLength, 1 ) ;
end ;
2011-05-30 10:03:04 +00:00
{ 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 ;
{ Other operations }
INT_EXCEL_TOKEN_TATTR: { fekOpSUM }
begin
// Uniry SUM Operation
AStream. WriteByte( $10 ) ;
AStream. WriteByte( 0 ) ;
AStream. WriteByte( 0 ) ;
Inc( RPNLength, 3 ) ;
end ;
2014-04-08 09:48:30 +00:00
// Functions with fixed parameter count
2013-02-12 12:13:49 +00:00
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
begin
2014-04-08 09:48:30 +00:00
AStream. WriteWord( WordToLE( lSecondaryID) ) ;
2013-02-12 12:13:49 +00:00
Inc( RPNLength, 2 ) ;
end ;
2014-04-08 09:48:30 +00:00
// Functions with variable parameter count
INT_EXCEL_TOKEN_FUNCVAR_V:
begin
AStream. WriteByte( AFormula[ i] . ParamsNum) ;
AStream. WriteWord( WordToLE( lSecondaryID) ) ;
Inc( RPNLength, 3 ) ;
end ;
2011-05-30 10:03:04 +00:00
else
2011-05-30 09:22:08 +00:00
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( 2 2 + RPNLength) ) ;
AStream. position : = FinalPos;
end ;
2008-02-24 13:18:34 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2009-04-21 15:08:43 +00:00
* TsSpreadBIFF8Writer. WriteIndex ( )
*
* DESCRIPTION: Writes an Excel 8 INDEX record
*
* nm = ( rl - rf - 1 ) / 3 2 + 1 ( using integer division)
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteIndex( AStream: TStream) ;
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_INDEX) ) ;
AStream. WriteWord( WordToLE( 1 6 ) ) ;
{ Not used }
AStream. WriteDWord( DWordToLE( 0 ) ) ;
{ Index to first used row, rf, 0 based }
AStream. WriteDWord( DWordToLE( 0 ) ) ;
{ Index to first row of unused tail of sheet, rl, last used row + 1, 0 based }
AStream. WriteDWord( DWordToLE( 0 ) ) ;
{ Absolute stream position of the DEFCOLWIDTH record of the current sheet.
If it doesn' t exist, the offset points to where it would occur. }
AStream. WriteDWord( DWordToLE( $00 ) ) ;
{ Array of nm absolute stream positions of the DBCELL record of each Row Block }
2013-12-07 13:42:22 +00:00
{ OBS: It seems to be no problem just ignoring this part of the record }
2009-04-21 15:08:43 +00:00
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteLabel ( )
2008-02-24 13:18:34 +00:00
*
* DESCRIPTION: Writes an Excel 8 LABEL record
*
* Writes a string to the sheet
2013-12-07 13:42:22 +00:00
* If the string length exceeds 3 2 7 5 8 bytes, the string
* will be silently truncated.
2008-02-24 13:18:34 +00:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Writer. WriteLabel( AStream: TStream; const ARow,
2013-12-07 13:42:22 +00:00
ACol: Cardinal ; const AValue: string ; ACell: PCell) ;
const
//limit for this format: 32767 bytes - header (see reclen below):
//37267-8-1=32758
MaxBytes= 3 2 7 5 8 ;
2008-02-24 13:18:34 +00:00
var
2011-05-27 10:20:53 +00:00
L, RecLen: Word ;
2013-12-07 13:42:22 +00:00
TextTooLong: boolean = false ;
2009-04-21 15:08:43 +00:00
WideValue: WideString ;
2008-02-24 13:18:34 +00:00
begin
2013-12-07 13:42:22 +00:00
WideValue : = UTF8Decode( AValue) ; //to UTF16
2009-04-21 15:08:43 +00:00
if WideValue = '' then
begin
2013-12-07 13:42:22 +00:00
// Badly formatted UTF8String (maybe ANSI?)
2009-04-21 15:08:43 +00:00
if Length( AValue) < > 0 then begin
2013-12-07 13:42:22 +00:00
//Quite sure it was an ANSI string written as UTF8, so raise exception.
2009-04-21 15:08:43 +00:00
Raise Exception. CreateFmt( 'Expected UTF8 text but probably ANSI text found in cell [%d,%d]' , [ ARow, ACol] ) ;
end ;
Exit;
end ;
2013-12-07 13:42:22 +00:00
if Length( WideValue) > MaxBytes then
begin
// Rather than lose data when reading it, let the application programmer deal
// with the problem or purposefully ignore it.
TextTooLong : = true ;
SetLength( WideValue, MaxBytes) ; //may corrupt the string (e.g. in surrogate pairs), but... too bad.
end ;
2009-04-21 15:08:43 +00:00
L : = Length( WideValue) ;
2008-02-24 13:18:34 +00:00
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_LABEL) ) ;
2013-12-07 13:42:22 +00:00
RecLen : = 8 + 1 + L * SizeOf( WideChar ) ;
2011-05-27 10:20:53 +00:00
AStream. WriteWord( WordToLE( RecLen) ) ;
2008-02-24 13:18:34 +00:00
{ BIFF Record data }
AStream. WriteWord( WordToLE( ARow) ) ;
AStream. WriteWord( WordToLE( ACol) ) ;
2010-07-30 06:44:53 +00:00
{ Index to XF record, according to formatting }
2011-05-25 15:50:18 +00:00
WriteXFIndex( AStream, ACell) ;
2009-04-21 15:08:43 +00:00
{ Byte String with 16-bit size }
AStream. WriteWord( WordToLE( L) ) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ Byte flags. 1 means regular Unicode LE encoding}
AStream. WriteByte( 1 ) ;
AStream. WriteBuffer( WideStringToLE( WideValue) [ 1 ] , L * Sizeof( WideChar ) ) ;
2013-12-07 13:42:22 +00:00
{
//todo: keep a log of errors and show with an exception after writing file or something.
We can' t just do the following
if TextTooLong then
Raise Exception. CreateFmt( 'Text value exceeds %d character limit in cell [%d,%d]. Text has been truncated.' , [ MaxBytes, ARow, ACol] ) ;
because the file wouldn' t be written.
}
2008-02-24 13:18:34 +00:00
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2009-04-21 15:08:43 +00:00
* TsSpreadBIFF8Writer. WriteNumber ( )
2008-02-24 13:18:34 +00:00
*
2009-04-21 15:08:43 +00:00
* DESCRIPTION: Writes an Excel 8 NUMBER record
2008-02-24 13:18:34 +00:00
*
* Writes a number ( 6 4 - bit floating point) to the sheet
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Writer. WriteNumber( AStream: TStream; const ARow,
2010-12-08 10:24:15 +00:00
ACol: Cardinal ; const AValue: double ; ACell: PCell) ;
2008-02-24 13:18:34 +00:00
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_NUMBER) ) ;
2013-12-07 13:42:22 +00:00
AStream. WriteWord( WordToLE( 1 4 ) ) ; //total record size
2008-02-24 13:18:34 +00:00
{ BIFF Record data }
AStream. WriteWord( WordToLE( ARow) ) ;
AStream. WriteWord( WordToLE( ACol) ) ;
2013-02-12 11:29:19 +00:00
{ Index to XF record, according to formatting }
WriteXFIndex( AStream, ACell) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ IEE 754 floating-point value (is different in BIGENDIAN???) }
2008-02-24 13:18:34 +00:00
AStream. WriteBuffer( AValue, 8 ) ;
end ;
2011-05-26 11:04:30 +00:00
procedure TsSpreadBIFF8Writer. WritePalette( AStream: TStream) ;
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_PALETTE) ) ;
AStream. WriteWord( WordToLE( 2 + 4 * 5 6 ) ) ;
{ Number of colors }
AStream. WriteWord( WordToLE( 5 6 ) ) ;
{ Now the colors, first the standard 16 from Excel }
2013-12-27 18:01:59 +00:00
AStream. WriteDWord( LongRGBToExcelPhysical( $000000 ) ) ; // $08
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FF0000 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $00FF00 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $0000FF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFF00 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FF00FF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $00FFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $800000 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $008000 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $000080 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $808000 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $800080 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $008080 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $C0C0C0 ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $808080 ) ) ; //$17
2011-05-26 11:04:30 +00:00
{ Now some colors which we define ourselves }
2013-12-27 18:01:59 +00:00
AStream. WriteDWord( LongRGBToExcelPhysical( $E6E6E6 ) ) ; //$18 //todo: shouldn't we write $18..$3F and add this color later? see 5.74.3 Built-In Default Colour Tables
AStream. WriteDWord( LongRGBToExcelPhysical( $CCCCCC ) ) ; //$19 //todo: shouldn't we write $18..$3F and add this color later? see 5.74.3 Built-In Default Colour Tables
2011-05-26 11:04:30 +00:00
{ And padding }
2013-12-27 18:01:59 +00:00
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ; //$20 //todo: is this still correct?
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ; //$30
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
AStream. WriteDWord( LongRGBToExcelPhysical( $FFFFFF ) ) ;
2011-05-26 11:04:30 +00:00
end ;
2009-04-21 15:08:43 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteStyle ( )
*
* DESCRIPTION: Writes an Excel 8 STYLE record
*
* Registers the name of a user- defined style or
* specific options for a built- in cell style.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteStyle( AStream: TStream) ;
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_STYLE) ) ;
AStream. WriteWord( WordToLE( 4 ) ) ;
{ Index to style XF and defines if it's a built-in or used defined style }
AStream. WriteWord( WordToLE( MASK_STYLE_BUILT_IN) ) ;
{ Built-in cell style identifier }
AStream. WriteByte( $00 ) ;
{ Level if the identifier for a built-in style is RowLevel or ColLevel, $FF otherwise }
AStream. WriteByte( $FF ) ;
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteWindow1 ( )
*
* DESCRIPTION: Writes an Excel 8 WINDOW1 record
*
* This record contains general settings for the
* document window and global workbook settings.
*
* The values written here are reasonable defaults,
* which should work for most sheets.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteWindow1( AStream: TStream) ;
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_WINDOW1) ) ;
AStream. WriteWord( WordToLE( 1 8 ) ) ;
{ Horizontal position of the document window, in twips = 1 / 20 of a point }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Vertical position of the document window, in twips = 1 / 20 of a point }
AStream. WriteWord( WordToLE( $0069 ) ) ;
{ Width of the document window, in twips = 1 / 20 of a point }
AStream. WriteWord( WordToLE( $339F ) ) ;
{ Height of the document window, in twips = 1 / 20 of a point }
AStream. WriteWord( WordToLE( $1B5D ) ) ;
{ Option flags }
AStream. WriteWord( WordToLE(
MASK_WINDOW1_OPTION_HORZ_SCROLL_VISIBLE or
MASK_WINDOW1_OPTION_VERT_SCROLL_VISIBLE or
MASK_WINDOW1_OPTION_WORKSHEET_TAB_VISIBLE) ) ;
{ Index to active (displayed) worksheet }
AStream. WriteWord( WordToLE( $00 ) ) ;
{ Index of first visible tab in the worksheet tab bar }
AStream. WriteWord( WordToLE( $00 ) ) ;
{ Number of selected worksheets }
AStream. WriteWord( WordToLE( 1 ) ) ;
{ Width of worksheet tab bar ( in 1 / 1 0 0 0 of window width) .
The remaining space is used by the horizontal scroll bar }
AStream. WriteWord( WordToLE( 6 0 0 ) ) ;
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteWindow2 ( )
*
* DESCRIPTION: Writes an Excel 8 WINDOW2 record
*
* This record contains aditional settings for the
* document window ( BIFF2- BIFF4) or for a specific
* worksheet ( BIFF5- BIFF8) .
*
* The values written here are reasonable defaults,
* which should work for most sheets.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteWindow2( AStream: TStream;
ASheetSelected: Boolean ) ;
var
Options: Word ;
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_WINDOW2) ) ;
AStream. WriteWord( WordToLE( 1 8 ) ) ;
{ Options flags }
Options : = MASK_WINDOW2_OPTION_SHOW_GRID_LINES or
MASK_WINDOW2_OPTION_SHOW_SHEET_HEADERS or
MASK_WINDOW2_OPTION_SHOW_ZERO_VALUES or
MASK_WINDOW2_OPTION_AUTO_GRIDLINE_COLOR or
MASK_WINDOW2_OPTION_SHOW_OUTLINE_SYMBOLS or
MASK_WINDOW2_OPTION_SHEET_ACTIVE;
if ASheetSelected then Options : = Options or MASK_WINDOW2_OPTION_SHEET_SELECTED;
AStream. WriteWord( WordToLE( Options) ) ;
{ Index to first visible row }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Index to first visible column }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Grid line index colour }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Not used }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Cached magnification factor in page break preview (in percent); 0 = Default (60%) }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Cached magnification factor in normal view (in percent); 0 = Default (100%) }
AStream. WriteWord( WordToLE( 0 ) ) ;
{ Not used }
AStream. WriteDWord( DWordToLE( 0 ) ) ;
end ;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TsSpreadBIFF8Writer. WriteXF ( )
*
* DESCRIPTION: Writes an Excel 8 XF record
*
*
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
procedure TsSpreadBIFF8Writer. WriteXF( AStream: TStream; AFontIndex: Word ;
2013-12-07 13:42:22 +00:00
AFormatIndex: Word ; AXF_TYPE_PROT, ATextRotation: Byte ; ABorders: TsCellBorders;
2011-05-26 10:00:56 +00:00
AddBackground: Boolean = False ; ABackgroundColor: TsColor = scSilver) ;
2009-04-21 15:08:43 +00:00
var
XFOptions: Word ;
XFAlignment, XFOrientationAttrib: Byte ;
2011-05-25 13:40:53 +00:00
XFBorderDWord1, XFBorderDWord2: DWord;
2008-02-24 13:18:34 +00:00
begin
{ BIFF Record header }
AStream. WriteWord( WordToLE( INT_EXCEL_ID_XF) ) ;
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( 2 0 ) ) ;
2008-02-24 13:18:34 +00:00
{ Index to FONT record }
2009-04-21 15:08:43 +00:00
AStream. WriteWord( WordToLE( AFontIndex) ) ;
2008-02-24 13:18:34 +00:00
{ Index to FORMAT record }
2013-12-07 13:42:22 +00:00
AStream. WriteWord( WordToLE( AFormatIndex) ) ;
2008-02-24 13:18:34 +00:00
{ XF type, cell protection and parent style XF }
2009-04-21 15:08:43 +00:00
XFOptions : = AXF_TYPE_PROT and MASK_XF_TYPE_PROT;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
if AXF_TYPE_PROT and MASK_XF_TYPE_PROT_STYLE_XF < > 0 then
XFOptions : = XFOptions or MASK_XF_TYPE_PROT_PARENT;
AStream. WriteWord( WordToLE( XFOptions) ) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ Alignment and text break }
XFAlignment : = MASK_XF_VERT_ALIGN_BOTTOM;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
AStream. WriteByte( XFAlignment) ;
{ Text rotation }
2010-07-30 06:44:53 +00:00
AStream. WriteByte( ATextRotation) ; // 0 is horizontal / normal
2009-04-21 15:08:43 +00:00
{ Indentation, shrink and text direction }
AStream. WriteByte( 0 ) ;
{ Used attributes }
XFOrientationAttrib : =
MASK_XF_USED_ATTRIB_NUMBER_FORMAT or
MASK_XF_USED_ATTRIB_FONT or
MASK_XF_USED_ATTRIB_TEXT or
MASK_XF_USED_ATTRIB_BORDER_LINES or
MASK_XF_USED_ATTRIB_BACKGROUND or
MASK_XF_USED_ATTRIB_CELL_PROTECTION;
AStream. WriteByte( XFOrientationAttrib) ;
2008-02-24 13:18:34 +00:00
2009-04-21 15:08:43 +00:00
{ Cell border lines and background area }
2011-05-25 13:40:53 +00:00
// Left and Right line colors, use black
XFBorderDWord1 : = 8 * $10000 {left line - black} + 8 * $800000 {right line - black} ;
if cbNorth in ABorders then XFBorderDWord1 : = XFBorderDWord1 or $100 ;
if cbWest in ABorders then XFBorderDWord1 : = XFBorderDWord1 or $1 ;
if cbEast in ABorders then XFBorderDWord1 : = XFBorderDWord1 or $10 ;
if cbSouth in ABorders then XFBorderDWord1 : = XFBorderDWord1 or $1000 ;
AStream. WriteDWord( DWordToLE( XFBorderDWord1) ) ;
// Top and Bottom line colors, use black
XFBorderDWord2 : = 8 {top line - black} + 8 * $80 {bottom line - black} ;
2011-05-26 09:39:17 +00:00
// Add a background, if desired
if AddBackground then XFBorderDWord2 : = XFBorderDWord2 or $4000000 ;
2011-05-25 13:40:53 +00:00
AStream. WriteDWord( DWordToLE( XFBorderDWord2) ) ;
// Background Pattern Color, always zeroed
2013-12-07 13:42:22 +00:00
if AddBackground then AStream. WriteWord( WordToLE( FPSColorToEXCELPalette( ABackgroundColor) ) )
2011-05-26 09:39:17 +00:00
else AStream. WriteWord( 0 ) ;
2009-04-21 15:08:43 +00:00
end ;
{ TsSpreadBIFF8Reader }
2009-09-01 23:00:13 +00:00
function TsSpreadBIFF8Reader. DecodeRKValue( const ARK: DWORD) : Double ;
var
Number: Double ;
Tmp: LongInt ;
begin
if ARK and 2 = 2 then begin
// Signed integer value
if LongInt( ARK) < 0 then begin
//Simulates a sar
Tmp: = LongInt( ARK) * - 1 ;
Tmp: = Tmp shr 2 ;
Tmp: = Tmp* - 1 ;
Number: = Tmp- 1 ;
end else begin
Number: = ARK shr 2 ;
end ;
end else begin
// Floating point value
// NOTE: This is endian dependent and IEEE dependent (Not checked) (working win-i386)
( PDWORD( @ Number) ) ^ : = $00000000 ;
( PDWORD( @ Number) + 1 ) ^ : = ( ARK and $FFFFFFFC ) ;
end ;
if ARK and 1 = 1 then begin
// Encoded value is multiplied by 100
Number: = Number / 1 0 0 ;
end ;
Result : = Number;
end ;
2013-12-22 14:02:04 +00:00
procedure TsSpreadBIFF8Reader. ExtractNumberFormat( AXFIndex: WORD ;
out ANumberFormat: TsNumberFormat; out ADecimals: Word ;
out ANumberFormatStr: String ) ;
const
{ see ➜ 5.49 }
NOT_USED = nfGeneral;
fmts: array [ 1 .. 5 8 ] of TsNumberFormat = (
nfFixed, nfFixed, nfFixedTh, nfFixedTh, nfFixedTh, // 1..5
nfFixedTh, nfFixedTh, nfFixedTh, nfPercentage, nfPercentage, // 6..10
nfExp, NOT_USED, NOT_USED, nfShortDate, nfShortDate, // 11..15
nfFmtDateTime, nfFmtDateTime, nfShortTimeAM, nfLongTimeAM, nfShortTime, // 16..20
nfLongTime, nfShortDateTime, NOT_USED, NOT_USED, NOT_USED, // 21..25
NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 26..30
NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 31..35
NOT_USED, nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, // 36..40
nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, nfFmtDateTime, // 41..45
nfTimeInterval, nfFmtDateTime, nfSci, NOT_USED, NOT_USED, // 46..50
NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 51..55
NOT_USED, NOT_USED, NOT_USED // 56..58
) ;
decs: array [ 1 .. 5 8 ] of word = (
0 , 2 , 0 , 2 , 0 , 0 , 2 , 2 , 0 , 2 , // 1..10
2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 11..20
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 21..30
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 2 , // 31..40
0 , 0 , 2 , 2 , 0 , 3 , 0 , 1 , 0 , 0 , // 41..50 #48 is "scientific", use "exponential" instead
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ; // 51..58
2013-12-07 13:42:22 +00:00
var
lFormatData: TFormatRecordData;
lXFData: TXFRecordData;
2013-12-22 14:02:04 +00:00
isAMPM: Boolean ;
isLongTime: Boolean ;
isMilliSec: Boolean ;
t, d: Boolean ;
2013-12-07 13:42:22 +00:00
begin
2013-12-22 14:02:04 +00:00
ANumberFormat : = nfGeneral;
ANumberFormatStr : = '' ;
ADecimals : = 0 ;
2013-12-07 13:42:22 +00:00
lFormatData : = FindFormatRecordForCell( AXFIndex) ;
{ Record FORMAT, BIFF8 ( 5.49 ) :
Offset Size Contents
0 2 Format index used in other records
}
2013-12-22 14:02:04 +00:00
if lFormatData = nil then begin
// no custom format, so first test for default formats
lXFData : = TXFRecordData ( FXFList. Items[ AXFIndex] ) ;
case lXFData. FormatIndex of
FORMAT_DATE_DM:
begin ANumberFormat : = nfFmtDateTime; ANumberFormatStr : = 'DM' ; end ;
FORMAT_DATE_MY:
begin ANumberFormat : = nfFmtDateTime; ANumberFormatStr : = 'MY' ; end ;
FORMAT_TIME_MS:
begin ANumberFormat : = nfFmtDateTime; ANumberFormatStr : = 'MS' ; end ;
FORMAT_TIME_MSZ:
begin ANumberFormat : = nfFmtDateTime; ANumberFormatStr : = 'MSZ' ; end ;
else
if ( lXFData. FormatIndex > 0 ) and ( lXFData. FormatIndex < = 5 8 ) then begin
ANumberFormat : = fmts[ lXFData. FormatIndex] ;
ADecimals : = decs[ lXFData. FormatIndex] ;
end ;
2013-12-07 13:42:22 +00:00
end ;
2013-12-22 14:02:04 +00:00
end else
// Check custom formats if they have / in format string (this can fail for
// custom text formats)
if IsPercentNumberFormat( lFormatData. FormatString, ADecimals) then
ANumberFormat : = nfPercentage
2013-12-07 13:42:22 +00:00
else
2013-12-22 14:02:04 +00:00
if IsExpNumberFormat( lFormatData. Formatstring, ADecimals) then
ANumberFormat : = nfExp
else
if IsThousandSepNumberFormat( lFormatData. FormatString, ADecimals) then
ANumberFormat : = nfFixedTh
else
if IsFixedNumberFormat( lFormatData. FormatString, ADecimals) then
ANumberFormat : = nfFixed
else begin
t : = IsTimeFormat( lFormatData. FormatString, isLongTime, isAMPM, isMilliSec) ;
d : = IsDateFormat( lFormatData. FormatString) ;
if d and t then
ANumberFormat : = nfShortDateTime
else
if d then
ANumberFormat : = nfShortDate
else
if t then begin
if isAMPM then begin
if isLongTime then
ANumberFormat : = nfLongTimeAM
else
ANumberFormat : = nfShortTimeAM;
end else begin
if isLongTime then
ANumberFormat : = nfLongTime
else
ANumberFormat : = nfShortTime;
end ;
2013-12-07 13:42:22 +00:00
end ;
end ;
2013-12-22 14:02:04 +00:00
end ;
function TsSpreadBIFF8Reader. IsDateTime( Number: Double ;
ANumberFormat: TsNumberFormat; var ADateTime: TDateTime) : boolean ;
// Convert the number to a date/time and return that if it is
begin
if ANumberFormat in [
nfShortDateTime, nfFmtDateTime, nfShortDate,
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM] then
begin
ADateTime : = ConvertExcelDateTimeToDateTime( Number, FDateMode) ;
Result : = true ;
end else
if ANumberFormat = nfTimeInterval then begin
ADateTime : = Number;
Result : = true ;
end else
begin
ADateTime : = 0 ;
Result : = false ;
end ;
2013-12-07 13:42:22 +00:00
end ;
2009-04-21 15:08:43 +00:00
function TsSpreadBIFF8Reader. ReadWideString( const AStream: TStream;
const ALength: WORD ) : WideString ;
var
StringFlags: BYTE ;
2011-12-25 00:04:53 +00:00
DecomprStrValue: WideString ;
AnsiStrValue: ansistring ;
2009-09-01 23:00:13 +00:00
RunsCounter: WORD ;
AsianPhoneticBytes: DWORD;
2011-12-25 00:04:53 +00:00
i: Integer ;
2009-09-01 23:00:13 +00:00
j: SizeUInt;
2011-12-25 00:04:53 +00:00
lLen: SizeInt;
2013-07-02 15:07:43 +00:00
RecordType: WORD ;
RecordSize: WORD ;
C: char ;
2009-04-21 15:08:43 +00:00
begin
StringFlags: = AStream. ReadByte;
2009-09-01 23:00:13 +00:00
Dec( PendingRecordSize) ;
if StringFlags and 4 = 4 then begin
//Asian phonetics
//Read Asian phonetics Length (not used)
AsianPhoneticBytes: = DWordLEtoN( AStream. ReadDWord) ;
end ;
if StringFlags and 8 = 8 then begin
//Rich string
RunsCounter: = WordLEtoN( AStream. ReadWord) ;
dec( PendingRecordSize, 2 ) ;
end ;
2009-04-21 15:08:43 +00:00
if StringFlags and 1 = 1 Then begin
//String is WideStringLE
2009-09-01 23:00:13 +00:00
if ( ALength* SizeOf( WideChar ) ) > PendingRecordSize then begin
2013-02-12 14:49:43 +00:00
SetLength( Result , PendingRecordSize div 2 ) ;
AStream. ReadBuffer( Result [ 1 ] , PendingRecordSize) ;
Dec( PendingRecordSize, PendingRecordSize) ;
2009-09-01 23:00:13 +00:00
end else begin
SetLength( Result , ALength) ;
AStream. ReadBuffer( Result [ 1 ] , ALength * SizeOf( WideChar ) ) ;
Dec( PendingRecordSize, ALength * SizeOf( WideChar ) ) ;
end ;
2009-04-21 15:08:43 +00:00
Result : = WideStringLEToN( Result ) ;
end else begin
2011-12-25 09:03:20 +00:00
//String is 1 byte per char, this is UTF-16 with the high byte ommited because it is zero
2013-07-02 15:07:43 +00:00
//so decompress and then convert
lLen: = ALength;
2011-12-25 00:04:53 +00:00
SetLength( DecomprStrValue, lLen) ;
for i : = 1 to lLen do
begin
2013-07-02 15:07:43 +00:00
C: = WideChar( AStream. ReadByte( ) ) ;
DecomprStrValue[ i] : = C;
Dec( PendingRecordSize) ;
if ( PendingRecordSize< = 0 ) and ( i< lLen) then begin
//A CONTINUE may happend here
RecordType : = WordLEToN( AStream. ReadWord) ;
RecordSize : = WordLEToN( AStream. ReadWord) ;
if RecordType< > INT_EXCEL_ID_CONTINUE then begin
Raise Exception. Create( '[TsSpreadBIFF8Reader.ReadWideString] Expected CONTINUE record not found.' ) ;
end else begin
PendingRecordSize: = RecordSize;
DecomprStrValue: = copy( DecomprStrValue, 1 , i) + ReadWideString( AStream, ALength- i) ;
break;
end ;
end ;
2009-09-01 23:00:13 +00:00
end ;
2011-12-25 00:04:53 +00:00
Result : = DecomprStrValue;
2009-04-21 15:08:43 +00:00
end ;
2009-09-01 23:00:13 +00:00
if StringFlags and 8 = 8 then begin
//Rich string (This only happend in BIFF8)
for j : = 1 to RunsCounter do begin
2013-07-02 15:07:43 +00:00
if ( PendingRecordSize< = 0 ) then begin
//A CONTINUE may happend here
RecordType : = WordLEToN( AStream. ReadWord) ;
RecordSize : = WordLEToN( AStream. ReadWord) ;
if RecordType< > INT_EXCEL_ID_CONTINUE then begin
Raise Exception. Create( '[TsSpreadBIFF8Reader.ReadWideString] Expected CONTINUE record not found.' ) ;
end else begin
PendingRecordSize: = RecordSize;
end ;
end ;
2009-09-01 23:00:13 +00:00
AStream. ReadWord;
AStream. ReadWord;
dec( PendingRecordSize, 2 * 2 ) ;
end ;
end ;
if StringFlags and 4 = 4 then begin
//Asian phonetics
//Read Asian phonetics, discarded as not used.
SetLength( AnsiStrValue, AsianPhoneticBytes) ;
AStream. ReadBuffer( AnsiStrValue[ 1 ] , AsianPhoneticBytes) ;
end ;
2009-04-21 15:08:43 +00:00
end ;
2012-01-23 13:24:13 +00:00
function TsSpreadBIFF8Reader. ReadWideString( const AStream: TStream;
const AUse8BitLength: Boolean ) : WideString ;
var
Len: Word ;
WideName: WideString ;
begin
if AUse8BitLength then
Len : = AStream. ReadByte( )
else
Len : = WordLEtoN( AStream. ReadWord( ) ) ;
Result : = ReadWideString( AStream, Len) ;
end ;
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Reader. ReadWorkbookGlobals( AStream: TStream;
AData: TsWorkbook) ;
var
SectionEOF: Boolean = False ;
RecordType: Word ;
CurStreamPos: Int64 ;
begin
2013-12-07 13:42:22 +00:00
if Assigned( FSharedStringTable) then FreeAndNil( FSharedStringTable) ;
2009-04-21 15:08:43 +00:00
while ( not SectionEOF) do
begin
{ Read the record header }
RecordType : = WordLEToN( AStream. ReadWord) ;
RecordSize : = WordLEToN( AStream. ReadWord) ;
2009-09-01 23:00:13 +00:00
PendingRecordSize: = RecordSize;
2009-04-21 15:08:43 +00:00
CurStreamPos : = AStream. Position;
2009-09-01 23:00:13 +00:00
if RecordType< > INT_EXCEL_ID_CONTINUE then begin
case RecordType of
INT_EXCEL_ID_BOF: ;
INT_EXCEL_ID_BOUNDSHEET: ReadBoundSheet( AStream) ;
INT_EXCEL_ID_EOF: SectionEOF : = True ;
INT_EXCEL_ID_SST: ReadSST( AStream) ;
2011-12-24 23:38:24 +00:00
INT_EXCEL_ID_CODEPAGE: ReadCodepage( AStream) ;
INT_EXCEL_ID_FONT: ReadFont( AStream) ;
2012-01-23 13:24:13 +00:00
INT_EXCEL_ID_XF: ReadXF( AStream) ;
INT_EXCEL_ID_FORMAT: ReadFormat( AStream) ;
2012-01-23 13:48:39 +00:00
INT_EXCEL_ID_DATEMODE: ReadDateMode( AStream) ;
2009-09-01 23:00:13 +00:00
else
// nothing
end ;
2009-04-21 15:08:43 +00:00
end ;
// Make sure we are in the right position for the next record
AStream. Seek( CurStreamPos + RecordSize, soFromBeginning) ;
// Check for the end of the file
if AStream. Position > = AStream. Size then SectionEOF : = True ;
end ;
2008-02-24 13:18:34 +00:00
end ;
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Reader. ReadWorksheet( AStream: TStream; AData: TsWorkbook) ;
var
SectionEOF: Boolean = False ;
RecordType: Word ;
CurStreamPos: Int64 ;
begin
FWorksheet : = AData. AddWorksheet( FWorksheetNames. Strings[ FCurrentWorksheet] ) ;
while ( not SectionEOF) do
begin
{ Read the record header }
RecordType : = WordLEToN( AStream. ReadWord) ;
RecordSize : = WordLEToN( AStream. ReadWord) ;
2011-05-27 10:20:53 +00:00
PendingRecordSize: = RecordSize;
2009-04-21 15:08:43 +00:00
CurStreamPos : = AStream. Position;
case RecordType of
INT_EXCEL_ID_NUMBER: ReadNumber( AStream) ;
INT_EXCEL_ID_LABEL: ReadLabel( AStream) ;
2013-02-13 07:38:57 +00:00
INT_EXCEL_ID_FORMULA: ReadFormula( AStream) ;
//(RSTRING) This record stores a formatted text cell (Rich-Text).
// In BIFF8 it is usually replaced by the LABELSST record. Excel still
// uses this record, if it copies formatted text cells to the clipboard.
INT_EXCEL_ID_RSTRING: ReadRichString( AStream) ;
// (RK) This record represents a cell that contains an RK value
// (encoded integer or floating-point value). If a floating-point
// value cannot be encoded to an RK value, a NUMBER record will be written.
// This record replaces the record INTEGER written in BIFF2.
INT_EXCEL_ID_RK: ReadRKValue( AStream) ;
2009-09-01 23:00:13 +00:00
INT_EXCEL_ID_MULRK: ReadMulRKValues( AStream) ;
INT_EXCEL_ID_LABELSST: ReadLabelSST( AStream) ; //BIFF8 only
2009-04-21 15:08:43 +00:00
INT_EXCEL_ID_BOF: ;
INT_EXCEL_ID_EOF: SectionEOF : = True ;
else
// nothing
end ;
// Make sure we are in the right position for the next record
AStream. Seek( CurStreamPos + RecordSize, soFromBeginning) ;
// Check for the end of the file
if AStream. Position > = AStream. Size then SectionEOF : = True ;
end ;
end ;
procedure TsSpreadBIFF8Reader. ReadBoundsheet( AStream: TStream) ;
var
Len: Byte ;
WideName: WideString ;
begin
{ Absolute stream position of the BOF record of the sheet represented
by this record }
// Just assume that they are in order
AStream. ReadDWord( ) ;
{ Visibility }
AStream. ReadByte( ) ;
{ Sheet type }
AStream. ReadByte( ) ;
{ Sheet name: 8-bit length }
Len : = AStream. ReadByte( ) ;
{ Read string with flags }
WideName: = ReadWideString( AStream, Len) ;
FWorksheetNames. Add( UTF8Encode( WideName) ) ;
end ;
2009-09-01 23:00:13 +00:00
procedure TsSpreadBIFF8Reader. ReadRKValue( const AStream: TStream) ;
var
RK: DWORD;
ARow, ACol, XF: WORD ;
2012-01-23 13:48:39 +00:00
lDateTime: TDateTime;
2013-12-07 13:42:22 +00:00
Number: Double ;
2013-12-22 14:02:04 +00:00
nf: TsNumberFormat; // Number format
nd: word ; // decimals
nfs: String ; // Number format string
2009-09-01 23:00:13 +00:00
begin
2013-12-07 13:42:22 +00:00
{Retrieve XF record, row and column}
2009-09-01 23:00:13 +00:00
ReadRowColXF( AStream, ARow, ACol, XF) ;
{Encoded RK value}
RK: = DWordLEtoN( AStream. ReadDWord) ;
{Check RK codes}
Number: = DecodeRKValue( RK) ;
2012-01-23 13:24:13 +00:00
2013-12-07 13:42:22 +00:00
{Find out what cell type, set contenttype and value}
2013-12-22 14:02:04 +00:00
ExtractNumberFormat( XF, nf, nd, nfs) ;
if IsDateTime( Number, nf, lDateTime) then
FWorksheet. WriteDateTime( ARow, ACol, lDateTime, nf, nfs)
2013-12-07 13:42:22 +00:00
else
2013-12-22 14:02:04 +00:00
FWorksheet. WriteNumber( ARow, ACol, Number, nf) ;
2009-09-01 23:00:13 +00:00
end ;
procedure TsSpreadBIFF8Reader. ReadMulRKValues( const AStream: TStream) ;
var
ARow, fc, lc, XF: Word ;
2013-12-07 13:42:22 +00:00
lDateTime: TDateTime;
2009-09-01 23:00:13 +00:00
Pending: integer ;
RK: DWORD;
Number: Double ;
2013-12-22 14:02:04 +00:00
nf: TsNumberFormat;
nd: word ;
nfs: String ;
2009-09-01 23:00:13 +00:00
begin
ARow: = WordLEtoN( AStream. ReadWord) ;
fc: = WordLEtoN( AStream. ReadWord) ;
Pending: = RecordSize- sizeof( fc) - Sizeof( ARow) ;
while Pending > ( sizeof( XF) + sizeof( RK) ) do begin
2013-12-07 13:42:22 +00:00
XF: = AStream. ReadWord; //XF record (used for date checking)
2009-09-01 23:00:13 +00:00
RK: = DWordLEtoN( AStream. ReadDWord) ;
Number: = DecodeRKValue( RK) ;
2013-12-07 13:42:22 +00:00
{Find out what cell type, set contenttype and value}
2013-12-22 14:02:04 +00:00
ExtractNumberFormat( XF, nf, nd, nfs) ;
if IsDateTime( Number, nf, lDateTime) then
FWorksheet. WriteDateTime( ARow, fc, lDateTime, nf, nfs)
2013-12-07 13:42:22 +00:00
else
2013-12-22 14:02:04 +00:00
FWorksheet. WriteNumber( ARow, fc, Number, nf, nd) ;
2009-09-01 23:00:13 +00:00
inc( fc) ;
dec( Pending, ( sizeof( XF) + sizeof( RK) ) ) ;
end ;
if Pending= 2 then begin
//Just for completeness
lc: = WordLEtoN( AStream. ReadWord) ;
if lc+ 1 < > fc then begin
//Stream error... bypass by now
end ;
end ;
end ;
procedure TsSpreadBIFF8Reader. ReadRowColXF( const AStream: TStream; out ARow,
ACol, AXF: WORD ) ;
begin
{ BIFF Record data }
ARow : = WordLEToN( AStream. ReadWord) ;
ACol : = WordLEToN( AStream. ReadWord) ;
{ Index to XF record }
AXF: = WordLEtoN( AStream. ReadWord) ;
end ;
function TsSpreadBIFF8Reader. ReadString( const AStream: TStream;
const ALength: WORD ) : UTF8String ;
begin
2011-12-24 23:38:24 +00:00
Result : = UTF16ToUTF8( ReadWideString( AStream, ALength) ) ;
2009-09-01 23:00:13 +00:00
end ;
2012-01-23 13:24:13 +00:00
constructor TsSpreadBIFF8Reader. Create;
begin
inherited Create;
FXFList : = TFPList. Create;
FFormatList : = TFPList. Create;
end ;
destructor TsSpreadBIFF8Reader. Destroy;
2012-03-30 19:25:26 +00:00
var
j: integer ;
2012-01-23 13:24:13 +00:00
begin
2012-03-30 19:25:26 +00:00
for j : = FXFList. Count- 1 downto 0 do TObject( FXFList[ j] ) . Free;
for j : = FFormatList. Count- 1 downto 0 do TObject( FFormatList[ j] ) . Free;
2012-01-23 13:24:13 +00:00
FXFList. Free;
FFormatList. Free;
if Assigned( FSharedStringTable) then FSharedStringTable. Free;
end ;
2009-04-21 15:08:43 +00:00
procedure TsSpreadBIFF8Reader. ReadFromFile( AFileName: string ; AData: TsWorkbook) ;
var
MemStream: TMemoryStream;
OLEStorage: TOLEStorage;
OLEDocument: TOLEDocument;
begin
MemStream : = TMemoryStream. Create;
OLEStorage : = TOLEStorage. Create;
try
// Only one stream is necessary for any number of worksheets
OLEDocument. Stream : = MemStream;
2009-05-10 17:59:25 +00:00
OLEStorage. ReadOLEFile( AFileName, OLEDocument, 'Workbook' ) ;
2009-04-21 15:08:43 +00:00
2010-05-25 09:11:02 +00:00
// Check if the operation succeded
if MemStream. Size = 0 then raise Exception. Create( 'FPSpreadsheet: Reading the OLE document failed' ) ;
2009-04-21 15:08:43 +00:00
// Rewind the stream and read from it
MemStream. Position : = 0 ;
ReadFromStream( MemStream, AData) ;
// Uncomment to verify if the data was correctly optained from the OLE file
// MemStream.SaveToFile(SysUtils.ChangeFileExt(AFileName, 'bin.xls'));
finally
MemStream. Free;
OLEStorage. Free;
end ;
end ;
procedure TsSpreadBIFF8Reader. ReadFromStream( AStream: TStream; AData: TsWorkbook) ;
var
BIFF8EOF: Boolean ;
begin
{ Initializations }
FWorksheetNames : = TStringList. Create;
FWorksheetNames. Clear;
FCurrentWorksheet : = 0 ;
BIFF8EOF : = False ;
{ Read workbook globals }
ReadWorkbookGlobals( AStream, AData) ;
// Check for the end of the file
if AStream. Position > = AStream. Size then BIFF8EOF : = True ;
{ Now read all worksheets }
while ( not BIFF8EOF) do
begin
2010-01-19 16:53:49 +00:00
//Safe to not read beyond assigned worksheet names.
if FCurrentWorksheet> FWorksheetNames. Count- 1 then break;
2009-04-21 15:08:43 +00:00
ReadWorksheet( AStream, AData) ;
// Check for the end of the file
if AStream. Position > = AStream. Size then BIFF8EOF : = True ;
// Final preparations
Inc( FCurrentWorksheet) ;
end ;
{ Finalizations }
FWorksheetNames. Free;
end ;
procedure TsSpreadBIFF8Reader. ReadFormula( AStream: TStream) ;
2013-02-13 07:38:57 +00:00
var
ARow, ACol, XF: WORD ;
ResultFormula: Double ;
Data: array [ 0 .. 7 ] of BYTE ;
Flags: WORD ;
FormulaSize: BYTE ;
i: Integer ;
2009-04-21 15:08:43 +00:00
begin
2013-02-13 07:38:57 +00:00
{ BIFF Record header }
{ BIFF Record data }
{ Index to XF Record }
ReadRowColXF( AStream, ARow, ACol, XF) ;
{ Result of the formula in IEE 754 floating-point value }
AStream. ReadBuffer( Data, Sizeof( Data) ) ;
{ Options flags }
Flags: = WordLEtoN( AStream. ReadWord) ;
2009-04-21 15:08:43 +00:00
2013-02-13 07:38:57 +00:00
{ Not used }
AStream. ReadDWord;
{ Formula size }
FormulaSize : = WordLEtoN( AStream. ReadWord) ;
2013-12-07 13:42:22 +00:00
{ Formula data, output as debug info }
2013-02-13 07:38:57 +00:00
{ Write( 'Formula Element: ' ) ;
for i : = 1 to FormulaSize do
Write( IntToHex( AStream. ReadByte, 2 ) + ' ' ) ;
WriteLn( '' ) ; }
//RPN data not used by now
AStream. Position: = AStream. Position+ FormulaSize;
if SizeOf( Double ) < > 8 then Raise Exception. Create( 'Double is not 8 bytes' ) ;
Move( Data[ 0 ] , ResultFormula, sizeof( Data) ) ;
FWorksheet. WriteNumber( ARow, ACol, ResultFormula) ;
2009-04-21 15:08:43 +00:00
end ;
procedure TsSpreadBIFF8Reader. ReadLabel( AStream: TStream) ;
var
L: Word ;
StringFlags: BYTE ;
ARow, ACol: Word ;
WideStrValue: WideString ;
AnsiStrValue: AnsiString ;
begin
{ BIFF Record data }
ARow : = WordLEToN( AStream. ReadWord) ;
ACol : = WordLEToN( AStream. ReadWord) ;
{ Index to XF record, not used }
AStream. ReadWord( ) ;
{ Byte String with 16-bit size }
L : = WordLEtoN( AStream. ReadWord( ) ) ;
{ Read string with flags }
WideStrValue: = ReadWideString( AStream, L) ;
{ Save the data }
2011-12-24 23:38:24 +00:00
FWorksheet. WriteUTF8Text( ARow, ACol, UTF16ToUTF8( WideStrValue) ) ;
2009-04-21 15:08:43 +00:00
end ;
procedure TsSpreadBIFF8Reader. ReadNumber( AStream: TStream) ;
2013-12-07 13:42:22 +00:00
// Tries to read number from stream and write result to worksheet.
// Needs to check if a number is actually a date format
2009-04-21 15:08:43 +00:00
var
2013-12-07 13:42:22 +00:00
ARow, ACol, XF: Word ;
2009-04-21 15:08:43 +00:00
AValue: Double ;
2013-12-07 13:42:22 +00:00
lDateTime: TDateTime;
2013-12-22 14:02:04 +00:00
nf: TsNumberFormat;
nd: word ;
nfs: String ;
2009-04-21 15:08:43 +00:00
begin
2013-12-07 13:42:22 +00:00
{Retrieve XF record, row and column}
ReadRowColXF( AStream, ARow, ACol, XF) ;
2009-04-21 15:08:43 +00:00
{ IEE 754 floating-point value }
AStream. ReadBuffer( AValue, 8 ) ;
2013-12-07 13:42:22 +00:00
{Find out what cell type, set contenttype and value}
2013-12-22 14:02:04 +00:00
ExtractNumberFormat( XF, nf, nd, nfs) ;
if IsDateTime( AValue, nf, lDateTime) then
FWorksheet. WriteDateTime( ARow, ACol, lDateTime, nf, nfs)
2013-12-07 13:42:22 +00:00
else
2013-12-22 14:02:04 +00:00
FWorksheet. WriteNumber( ARow, ACol, AValue, nf, nd) ;
2009-04-21 15:08:43 +00:00
end ;
2009-09-01 23:00:13 +00:00
procedure TsSpreadBIFF8Reader. ReadRichString( const AStream: TStream) ;
var
L: Word ;
B: WORD ;
ARow, ACol, XF: Word ;
AStrValue: ansistring ;
begin
ReadRowColXF( AStream, ARow, ACol, XF) ;
{ Byte String with 16-bit size }
L : = WordLEtoN( AStream. ReadWord( ) ) ;
AStrValue: = ReadString( AStream, L) ;
{ Save the data }
FWorksheet. WriteUTF8Text( ARow, ACol, AStrValue) ;
//Read formatting runs (not supported)
B: = WordLEtoN( AStream. ReadWord) ;
for L : = 0 to B- 1 do begin
AStream. ReadWord; // First formatted character
AStream. ReadWord; // Index to FONT record
end ;
end ;
procedure TsSpreadBIFF8Reader. ReadSST( const AStream: TStream) ;
var
Items: DWORD;
2013-02-12 14:49:43 +00:00
StringLength, CurStrLen: WORD ;
2009-09-01 23:00:13 +00:00
LString: String ;
2011-11-02 22:33:19 +00:00
ContinueIndicator: WORD ;
2009-09-01 23:00:13 +00:00
begin
//Reads the shared string table, only compatible with BIFF8
if not Assigned( FSharedStringTable) then begin
//First time SST creation
FSharedStringTable: = TStringList. Create;
DWordLEtoN( AStream. ReadDWord) ; //Apparences not used
Items: = DWordLEtoN( AStream. ReadDWord) ;
Dec( PendingRecordSize, 8 ) ;
end else begin
//A second record must not happend. Garbage so skip.
Exit;
end ;
while Items> 0 do begin
StringLength: = 0 ;
StringLength: = WordLEtoN( AStream. ReadWord) ;
Dec( PendingRecordSize, 2 ) ;
LString: = '' ;
2013-02-12 14:49:43 +00:00
// This loop takes care of the string being split between the STT and the CONTINUE, or between CONTINUE records
while PendingRecordSize> 0 do
begin
if StringLength> 0 then
begin
2010-03-15 19:05:59 +00:00
//Read a stream of zero length reads all the stream.
2013-02-12 14:49:43 +00:00
LString: = LString+ ReadString( AStream, StringLength) ;
end
else
begin
2011-11-02 22:33:19 +00:00
//String of 0 chars in length, so just read it empty, reading only the mandatory flags
AStream. ReadByte; //And discard it.
Dec( PendingRecordSize) ;
//LString:=LString+'';
2010-03-15 19:05:59 +00:00
end ;
2013-02-12 14:49:43 +00:00
// Check if the record finished and we need a CONTINUE record to go on
if ( PendingRecordSize< = 0 ) and ( Items> 1 ) then
begin
2011-11-02 22:33:19 +00:00
//A Continue will happend, read the
2009-09-01 23:00:13 +00:00
//tag and continue linking...
2011-11-02 22:33:19 +00:00
ContinueIndicator: = WordLEtoN( AStream. ReadWord) ;
if ContinueIndicator< > INT_EXCEL_ID_CONTINUE then begin
2013-02-12 14:49:43 +00:00
Raise Exception. Create( '[TsSpreadBIFF8Reader.ReadSST] Expected CONTINUE record not found.' ) ;
2009-09-01 23:00:13 +00:00
end ;
PendingRecordSize: = WordLEtoN( AStream. ReadWord) ;
2013-02-12 14:49:43 +00:00
CurStrLen : = Length( UTF8ToUTF16( LString) ) ;
if StringLength< CurStrLen then Exception. Create( '[TsSpreadBIFF8Reader.ReadSST] StringLength<CurStrLen' ) ;
Dec( StringLength, CurStrLen) ; //Dec the used chars
2011-11-02 22:33:19 +00:00
if StringLength= 0 then break;
2009-09-01 23:00:13 +00:00
end else begin
break;
end ;
end ;
FSharedStringTable. Add( LString) ;
2014-04-08 09:53:02 +00:00
{$ifdef FPSPREADDEBUG}
2011-12-24 23:38:24 +00:00
WriteLn( 'Adding shared string: ' + LString) ;
{$endif}
2009-09-01 23:00:13 +00:00
dec( Items) ;
end ;
end ;
procedure TsSpreadBIFF8Reader. ReadLabelSST( const AStream: TStream) ;
var
ACol, ARow, XF: WORD ;
SSTIndex: DWORD;
begin
ReadRowColXF( AStream, ARow, ACol, XF) ;
SSTIndex: = DWordLEtoN( AStream. ReadDWord) ;
if SizeInt( SSTIndex) > = FSharedStringTable. Count then begin
Raise Exception. CreateFmt( 'Index %d in SST out of range (0-%d)' , [ Integer( SSTIndex) , FSharedStringTable. Count- 1 ] ) ;
end ;
FWorksheet. WriteUTF8Text( ARow, ACol, FSharedStringTable[ SSTIndex] ) ;
end ;
2012-01-23 13:24:13 +00:00
procedure TsSpreadBIFF8Reader. ReadXF( const AStream: TStream) ;
var
lData: TXFRecordData;
begin
lData : = TXFRecordData. Create;
// Record XF, BIFF8:
// Offset Size Contents
2013-12-07 13:42:22 +00:00
// 0 2 Index to FONT record (➜5.45))
2012-01-23 13:24:13 +00:00
WordLEtoN( AStream. ReadWord) ;
2013-12-07 13:42:22 +00:00
// 2 2 Index to FORMAT record (➜5.49))
2012-01-23 13:24:13 +00:00
lData. FormatIndex : = WordLEtoN( AStream. ReadWord) ;
2013-12-07 13:42:22 +00:00
{ Offset Size Contents
4 2 XF type , cell protection, and parent style XF:
2012-01-23 13:24:13 +00:00
Bit Mask Contents
2 - 0 0007 H XF_TYPE_PROT – XF type , cell protection ( see above)
1 5 - 4 FFF0H Index to parent style XF ( always FFFH in style XFs)
6 1 Alignment and text break:
Bit Mask Contents
2 - 0 07 H XF_HOR_ALIGN – Horizontal alignment ( see above)
3 08 H 1 = Text is wrapped at right border
6 - 4 70 H XF_VERT_ALIGN – Vertical alignment ( see above)
7 80 H 1 = Justify last line in justified or distibuted text
7 1 XF_ROTATION: Text rotation angle ( see above)
8 1 Indentation, shrink to cell size, and text direction:
Bit Mask Contents
3 - 0 0F H Indent level
4 10 H 1 = Shrink content to fit into cell
7 - 6 C0H Text direction:
0 = According to context
3 5
; 1 = Left- to - right; 2 = Right- to - left
9 1 Flags for used attribute groups:
.. .. }
// Add the XF to the list
FXFList. Add( lData) ;
end ;
procedure TsSpreadBIFF8Reader. ReadFormat( const AStream: TStream) ;
var
lData: TFormatRecordData;
begin
lData : = TFormatRecordData. Create;
2013-12-07 13:42:22 +00:00
// Record FORMAT, BIFF8 (5.49):
2012-01-23 13:24:13 +00:00
// Offset Size Contents
// 0 2 Format index used in other records
2013-12-07 13:42:22 +00:00
// From BIFF5 on: indexes 0..163 are built in
2012-01-23 13:24:13 +00:00
lData. Index : = WordLEtoN( AStream. ReadWord) ;
// 2 var. Number format string (Unicode string, 16-bit string length, ➜2.5.3)
lData. FormatString : = ReadWideString( AStream, False ) ;
// Add to the list
FFormatList. Add( lData) ;
end ;
2013-12-07 13:42:22 +00:00
function TsSpreadBIFF8Reader. FindFormatRecordForCell( const AXFIndex: Integer
2012-01-23 13:24:13 +00:00
) : TFormatRecordData;
var
lXFData: TXFRecordData;
lFormatData: TFormatRecordData;
i: Integer ;
begin
Result : = nil ;
2013-12-07 13:42:22 +00:00
lXFData : = TXFRecordData( FXFList. Items[ AXFIndex] ) ;
2012-01-23 13:24:13 +00:00
for i : = 0 to FFormatList. Count- 1 do
begin
lFormatData : = TFormatRecordData( FFormatList. Items[ i] ) ;
if lFormatData. Index = lXFData. FormatIndex then Exit( lFormatData) ;
end ;
end ;
2011-12-24 23:38:24 +00:00
procedure TsSpreadBIFF8Reader. ReadFont( const AStream: TStream) ;
var
lCodePage: Word ;
lHeight: Word ;
lOptions: Word ;
Len: Byte ;
lFontName: UTF8String ;
begin
{ Height of the font in twips = 1/20 of a point }
lHeight : = AStream. ReadWord( ) ; // WordToLE(200)
{ Option flags }
lOptions : = AStream. ReadWord( ) ;
{ Colour index }
AStream. ReadWord( ) ;
{ Font weight }
AStream. ReadWord( ) ;
{ Escapement type }
AStream. ReadWord( ) ;
{ Underline type }
AStream. ReadByte( ) ;
{ Font family }
AStream. ReadByte( ) ;
{ Character set }
lCodepage : = AStream. ReadByte( ) ;
2014-04-08 09:53:02 +00:00
{$ifdef FPSPREADDEBUG}
2011-12-24 23:38:24 +00:00
WriteLn( 'Reading Font Codepage=' + IntToStr( lCodepage) ) ;
{$endif}
{ Not used }
AStream. ReadByte( ) ;
{ Font name: Unicodestring, char count in 1 byte }
Len : = AStream. ReadByte( ) ;
lFontName : = ReadString( AStream, Len) ;
end ;
2009-04-21 15:08:43 +00:00
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Initialization section
*
* Registers this reader / writer on fpSpreadsheet
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
initialization
2009-05-10 17:59:25 +00:00
RegisterSpreadFormat( TsSpreadBIFF8Reader, TsSpreadBIFF8Writer, sfExcel8) ;
2009-04-21 15:08:43 +00:00
2008-02-24 13:18:34 +00:00
end .