fpspreadsheet: Add most of the BIFF8 features to BIFF5. Add test cases for BIFF5 --> pass. Move more shared code from xlsbiff5 and xlsbiff8 to xlscommon. Update wiki.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2971 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-04-25 22:15:26 +00:00
parent 402f527cbb
commit 7c2851cd98
7 changed files with 868 additions and 487 deletions

View File

@ -155,17 +155,17 @@ type
So 90 degrees clockwise means that the text will be:
| A
| B
\|/ C
v C
And 90 degree counter clockwise will be:
/|\ C
^ C
| B
| A
}
TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation,
rt90DegreeCounterClockwiseRotation);
rt90DegreeCounterClockwiseRotation, rtStacked);
{@@ Indicates the border for a cell }

View File

@ -31,6 +31,18 @@ type
{ BIFF2 file format tests }
procedure TestWriteReadBIFF2_Font_InternalPal; // internal palette for BIFF2 file format
{ BIFF5 file format tests }
// Background colors...
procedure TestWriteReadBIFF5_Background_InternalPal; // internal palette
procedure TestWriteReadBIFF5_Background_Biff5Pal; // official biff5 palette
procedure TestWriteReadBIFF5_Background_Biff8Pal; // official biff8 palette
procedure TestWriteReadBIFF5_Background_RandomPal; // palette 64, top 56 entries random
// Font colors...
procedure TestWriteReadBIFF5_Font_InternalPal; // internal palette for BIFF8 file format
procedure TestWriteReadBIFF5_Font_Biff5Pal; // official biff5 palette in BIFF8 file format
procedure TestWriteReadBIFF5_Font_Biff8Pal; // official biff8 palette in BIFF8 file format
procedure TestWriteReadBIFF5_Font_RandomPal; // palette 64, top 56 entries random
{ BIFF8 file format tests }
// Background colors...
procedure TestWriteReadBIFF8_Background_InternalPal; // internal palette
@ -250,6 +262,47 @@ begin
TestWriteReadFontColors(sfExcel2, 0);
end;
{ Tests for BIFF5 file format }
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Background_InternalPal;
begin
TestWriteReadBackgroundColors(sfExcel5, 0);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Background_Biff5Pal;
begin
TestWriteReadBackgroundColors(sfExcel5, 5);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Background_Biff8Pal;
begin
TestWriteReadBackgroundColors(sfExcel5, 8);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Background_RandomPal;
begin
TestWriteReadBackgroundColors(sfExcel5, 999);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Font_InternalPal;
begin
TestWriteReadFontColors(sfExcel5, 0);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Font_Biff5Pal;
begin
TestWriteReadFontColors(sfExcel5, 5);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Font_Biff8Pal;
begin
TestWriteReadFontColors(sfExcel5, 8);
end;
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF5_Font_RandomPal;
begin
TestWriteReadFontColors(sfExcel5, 999);
end;
{ Tests for BIFF8 file format }
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF8_Background_InternalPal;
begin

View File

@ -43,6 +43,12 @@ type
procedure TestWriteReadFontBIFF2_TimesNewRoman;
procedure TestWriteReadFontBIFF2_CourierNew;
// BIFF5 test cases
procedure TestWriteReadBoldBIFF5;
procedure TestWriteReadFontBIFF5_Arial;
procedure TestWriteReadFontBIFF5_TimesNewRoman;
procedure TestWriteReadFontBIFF5_CourierNew;
// BIFF8 test cases
procedure TestWriteReadBoldBIFF8;
procedure TestWriteReadFontBIFF8_Arial;
@ -189,6 +195,11 @@ begin
TestWriteReadBold(sfExcel2);
end;
procedure TSpreadWriteReadFontTests.TestWriteReadBoldBIFF5;
begin
TestWriteReadBold(sfExcel5);
end;
procedure TSpreadWriteReadFontTests.TestWriteReadBoldBIFF8;
begin
TestWriteReadBold(sfExcel8);
@ -270,6 +281,7 @@ begin
DeleteFile(TempFile);
end;
{ BIFF2 }
procedure TSpreadWriteReadFontTests.TestWriteReadFontBIFF2_Arial;
begin
TestWriteReadFont(sfExcel2, 'Arial');
@ -285,6 +297,23 @@ begin
TestWriteReadFont(sfExcel2, 'CourierNew');
end;
{ BIFF5 }
procedure TSpreadWriteReadFontTests.TestWriteReadFontBIFF5_Arial;
begin
TestWriteReadFont(sfExcel5, 'Arial');
end;
procedure TSpreadWriteReadFontTests.TestWriteReadFontBIFF5_TimesNewRoman;
begin
TestWriteReadFont(sfExcel5, 'TimesNewRoman');
end;
procedure TSpreadWriteReadFontTests.TestWriteReadFontBIFF5_CourierNew;
begin
TestWriteReadFont(sfExcel5, 'CourierNew');
end;
{ BIFF8 }
procedure TSpreadWriteReadFontTests.TestWriteReadFontBIFF8_Arial;
begin
TestWriteReadFont(sfExcel8, 'Arial');

View File

@ -49,6 +49,8 @@ type
procedure TestWriteReadBorder(AFormat: TsSpreadsheetFormat);
// Test column widths
procedure TestWriteReadColWidths(AFormat: TsSpreadsheetFormat);
// Test word wrapping
procedure TestWriteReadWordWrap(AFormat: TsSpreadsheetFormat);
published
// Writes out numbers & reads back.
@ -56,18 +58,23 @@ type
{ BIFF2 Tests }
procedure TestWriteReadBIFF2_Alignment;
procedure TestWriteReadBIFF2_ColWidths;
procedure TestWriteReadBIFF2_Border;
procedure TestWriteReadBIFF2_ColWidths;
{ BIFF5 Tests }
procedure TestWriteReadBIFF5_Alignment;
procedure TestWriteReadBIFF5_Border;
procedure TestWriteReadBIFF5_ColWidths;
procedure TestWriteReadBIFF5_WordWrap;
{ BIFF8 Tests }
procedure TestWriteReadBIFF8_Alignment;
procedure TestWriteReadBIFF8_ColWidths;
procedure TestWriteReadBIFF8_Border;
procedure TestWriteReadBIFF8_ColWidths;
procedure TestWriteReadBIFF8_WordWrap;
procedure TestWriteReadNumberFormats;
// Repeat with date/times
procedure TestWriteReadDateTimeFormats;
// Test word wrapping
procedure TestWriteReadWordWrap;
end;
implementation
@ -81,6 +88,7 @@ const
ColWidthSheet = 'ColWidths';
BordersSheet = 'CellBorders';
AlignmentSheet = 'TextAlignments';
WordwrapSheet = 'Wordwrap';
// Initialize array with variables that represent the values
// we expect to be in the test spreadsheet files.
@ -364,6 +372,11 @@ begin
TestWriteReadAlignment(sfExcel2);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF5_Alignment;
begin
TestWriteReadAlignment(sfExcel5);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF8_Alignment;
begin
TestWriteReadAlignment(sfExcel8);
@ -428,6 +441,11 @@ begin
TestWriteReadBorder(sfExcel2);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF5_Border;
begin
TestWriteReadBorder(sfExcel5);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF8_Border;
begin
TestWriteReadBorder(sfExcel8);
@ -485,12 +503,17 @@ begin
TestWriteReadColWidths(sfExcel2);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF5_ColWidths;
begin
TestWriteReadColWidths(sfExcel5);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF8_ColWidths;
begin
TestWriteReadColWidths(sfExcel8);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadWordWrap;
procedure TSpreadWriteReadFormatTests.TestWriteReadWordWrap(AFormat: TsSpreadsheetFormat);
const
LONGTEXT = 'This is a very, very, very, very long text.';
var
@ -507,7 +530,7 @@ begin
// Write out all test values:
// Cell A1 is word-wrapped, Cell B1 is NOT word-wrapped
MyWorkbook := TsWorkbook.Create;
MyWorkSheet:= MyWorkBook.AddWorksheet(FmtNumbersSheet);
MyWorkSheet:= MyWorkBook.AddWorksheet(WordwrapSheet);
MyWorksheet.WriteUTF8Text(0, 0, LONGTEXT);
MyWorksheet.WriteUsedFormatting(0, 0, [uffWordwrap]);
MyCell := MyWorksheet.FindCell(0, 0);
@ -520,13 +543,16 @@ begin
if MyCell = nil then
fail('Error in test code. Failed to get word-wrapped cell.');
CheckEquals((uffWordWrap in MyCell^.UsedFormattingFields), false, 'Test unsaved non-wrapped cell mismatch, cell ' + CellNotation(MyWorksheet,0,0));
MyWorkBook.WriteToFile(TempFile,sfExcel8,true);
MyWorkBook.WriteToFile(TempFile, AFormat, true);
MyWorkbook.Free;
// Open the spreadsheet, as biff8
MyWorkbook := TsWorkbook.Create;
MyWorkbook.ReadFromFile(TempFile, sfExcel8);
MyWorksheet:=GetWorksheetByName(MyWorkBook, FmtNumbersSheet);
MyWorkbook.ReadFromFile(TempFile, AFormat);
if AFormat = sfExcel2 then
MyWorksheet := MyWorkbook.GetFirstWorksheet
else
MyWorksheet := GetWorksheetByName(MyWorkBook, WordwrapSheet);
if MyWorksheet=nil then
fail('Error in test code. Failed to get named worksheet');
MyCell := MyWorksheet.FindCell(0, 0);
@ -542,6 +568,16 @@ begin
DeleteFile(TempFile);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF5_Wordwrap;
begin
TestWriteReadWordwrap(sfExcel5);
end;
procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF8_Wordwrap;
begin
TestWriteReadWordwrap(sfExcel8);
end;
initialization
RegisterTest(TSpreadWriteReadFormatTests);
InitSollFmtData;

View File

@ -78,15 +78,14 @@ type
TsSpreadBIFF5Reader = class(TsSpreadBIFFReader)
private
RecordSize: Word;
FWorksheet: TsWorksheet;
FWorksheetNames: TStringList;
FCurrentWorksheet: Integer;
protected
{ Helpers }
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer);
function DecodeRKValue(const ARK: DWORD): Double;
{ Record writing methods }
procedure ReadBlank(AStream: TStream); override;
procedure ReadFont(const AStream: TStream);
procedure ReadFormula(AStream: TStream); override;
procedure ReadFormulaExcel(AStream: TStream);
procedure ReadLabel(AStream: TStream); override;
@ -97,7 +96,7 @@ type
procedure ReadBoundsheet(AStream: TStream);
procedure ReadRichString(AStream: TStream);
procedure ReadRKValue(AStream: TStream);
procedure ReadRowColXF(const AStream: TStream; out ARow,ACol,AXF: WORD); virtual;
procedure ReadXF(AStream: TStream);
public
{ General reading methods }
procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override;
@ -110,28 +109,36 @@ type
private
WorkBookEncoding: TsEncoding;
protected
procedure AddDefaultFormats; override;
{ Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override;
procedure WriteBOF(AStream: TStream; ADataType: Word);
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
//procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding); this is in xlscommon
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override;
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TFPCustomFont);
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
const AFormula: TsRPNFormula; ACell: PCell); override;
procedure WriteFont(AStream: TStream; AFont: TsFont);
procedure WriteFonts(AStream: TStream);
procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: string; ACell: PCell); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: double; ACell: PCell); override;
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
const AFormula: TsRPNFormula; ACell: PCell); override;
procedure WriteStyle(AStream: TStream);
procedure WriteWindow1(AStream: TStream);
procedure WriteWindow2(AStream: TStream; ASheetSelected: Boolean);
procedure WriteXF(AStream: TStream; AFontIndex: Word; AXF_TYPE_PROT: Byte);
procedure WriteXF(AStream: TStream; AFontIndex: Word;
AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders;
AHorAlignment: TsHorAlignment = haDefault; AVertAlignment: TsVertAlignment = vaDefault;
AWordWrap: Boolean = false; AddBackground: Boolean = false;
ABackgroundColor: TsColor = scSilver);
procedure WriteXFFieldsForFormattingStyles(AStream: TStream);
procedure WriteXFIndex(AStream: TStream; ACell: PCell);
procedure WriteXFRecords(AStream: TStream);
public
{ General writing methods }
procedure WriteToFile(const AFileName: string;
@ -307,43 +314,40 @@ const
{ 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 substructures --- see xlscommon! }
XF_ROTATION_HORIZONTAL = 0;
XF_ROTATION_STACKED = 1;
XF_ROTATION_90DEG_CCW = 2;
XF_ROTATION_90DEG_CW = 3;
{ 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
{ XF CELL BORDER }
MASK_XF_BORDER_LEFT = $00000038;
MASK_XF_BORDER_RIGHT = $000001C0;
MASK_XF_BORDER_TOP = $00000007;
MASK_XF_BORDER_BOTTOM = $01C00000;
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 CELL BACKGROUND }
MASK_XF_BKGR_PATTERN_COLOR = $0000007F;
MASK_XF_BKGR_BACKGROUND_COLOR = $00003F80;
MASK_XF_BKGR_FILLPATTERN = $003F0000;
{ 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;
{ XF record constants }
MASK_XF_TYPE_PROT = $0007;
MASK_XF_TYPE_PROT_PARENT = $FFF0;
MASK_XF_VERT_ALIGN = $70;
{
Exported functions
}
{ TsSpreadBIFF5Writer }
procedure TsSpreadBIFF5Writer.AddDefaultFormats();
begin
NextXFIndex := 16;
SetLength(FFormattingStyles, 1);
// XF0..XF14: Normal style, Row Outline level 1..7,
// Column Outline level 1..7.
// XF15 - Default cell format, no formatting (4.6.2)
FFormattingStyles[0].UsedFormattingFields := [];
FFormattingStyles[0].Row := 15;
end;
{*******************************************************************
* TsSpreadBIFF5Writer.WriteToFile ()
*
@ -392,11 +396,10 @@ end;
*******************************************************************}
procedure TsSpreadBIFF5Writer.WriteToStream(AStream: TStream);
var
FontData: TFPCustomFont;
MyData: TMemoryStream;
CurrentPos: Int64;
Boundsheets: array of Int64;
i, len: Integer;
sheet : TsWorksheet;
begin
{ Store some data about the workbook that other routines need }
WorkBookEncoding := Workbook.Encoding;
@ -404,60 +407,12 @@ begin
{ Write workbook globals }
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
WriteCodepage(AStream, WorkBookEncoding);
WriteWindow1(AStream);
FontData := TFPCustomFont.Create;
try
FontData.Name := 'Arial';
// FONT0
WriteFont(AStream, FontData);
// FONT1
WriteFont(AStream, FontData);
// FONT2
WriteFont(AStream, FontData);
// FONT3
WriteFont(AStream, FontData);
// FONT5
WriteFont(AStream, FontData);
finally
FontData.Free;
end;
// XF0
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF1
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF2
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF3
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF4
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF5
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF6
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF7
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF8
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF9
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF10
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF11
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF12
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF13
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF14
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
// XF15
WriteXF(AStream, 0, 0);
WriteFonts(AStream);
WritePalette(AStream);
WriteXFRecords(AStream);
WriteStyle(AStream);
// A BOUNDSHEET for each worksheet
@ -474,6 +429,8 @@ begin
for i := 0 to Workbook.GetWorksheetCount - 1 do
begin
sheet := Workbook.GetWorksheetByIndex(i);
{ First goes back and writes the position of the BOF of the
sheet on the respective BOUNDSHEET record }
CurrentPos := AStream.Position;
@ -482,13 +439,11 @@ begin
AStream.Position := CurrentPos;
WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream);
WriteDimensions(AStream, Workbook.GetWorksheetByIndex(i));
WriteWindow2(AStream, True);
WriteCellsToStream(AStream, Workbook.GetWorksheetByIndex(i).Cells);
WriteIndex(AStream);
WriteColInfos(AStream, sheet);
WriteDimensions(AStream, sheet);
WriteWindow2(AStream, True);
WriteCellsToStream(AStream, sheet.Cells);
WriteEOF(AStream);
end;
@ -515,7 +470,7 @@ begin
AStream.WriteWord(WordToLE(ACol));
{ Index to XF record }
AStream.WriteWord(WordToLE(15));
WriteXFIndex(AStream, ACell);
end;
{*******************************************************************
@ -647,33 +602,53 @@ end;
* The font data is passed in an instance of TFPCustomFont
*
*******************************************************************}
procedure TsSpreadBIFF5Writer.WriteFont(AStream: TStream; AFont: TFPCustomFont);
procedure TsSpreadBIFF5Writer.WriteFont(AStream: TStream; AFont: TsFont);
var
Len: Byte;
optn: Word;
begin
Len := Length(AFont.Name);
if AFont = nil then // this happens for FONT4 in case of BIFF
exit;
if AFont.FontName = '' then
raise Exception.Create('Font name not specified.');
if AFont.Size <= 0.0 then
raise Exception.Create('Font size not specified.');
Len := Length(AFont.FontName);
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
AStream.WriteWord(WordToLE(14 + 1 + Len));
{ Height of the font in twips = 1/20 of a point }
AStream.WriteWord(WordToLE(200));
AStream.WriteWord(WordToLE(round(AFont.Size*20)));
{ Option flags }
AStream.WriteWord(0);
optn := 0;
if fssBold in AFont.Style then optn := optn or $0001;
if fssItalic in AFont.Style then optn := optn or $0002;
if fssUnderline in AFont.Style then optn := optn or $0004;
if fssStrikeout in AFont.Style then optn := optn or $0008;
AStream.WriteWord(WordToLE(optn));
{ Colour index }
AStream.WriteWord($7FFF);
AStream.WriteWord(WordToLE(ord(AFont.Color)));
{ Font weight }
AStream.WriteWord(WordToLE(INT_FONT_WEIGHT_NORMAL));
if fssBold in AFont.Style then
AStream.WriteWord(WordToLE(INT_FONT_WEIGHT_BOLD))
else
AStream.WriteWord(WordToLE(INT_FONT_WEIGHT_NORMAL));
{ Escapement type }
AStream.WriteWord(0);
{ Underline type }
AStream.WriteByte(0);
if fssUnderline in AFont.Style then
AStream.WriteByte(1)
else
AStream.WriteByte(0);
{ Font family }
AStream.WriteByte(0);
@ -686,11 +661,26 @@ begin
{ Font name: Byte string, 8-bit length }
AStream.WriteByte(Len);
AStream.WriteBuffer(AFont.Name[1], Len);
AStream.WriteBuffer(AFont.FontName[1], Len);
end;
{*******************************************************************
* TsSpreadBIFF5Writer.WriteFormula ()
* TsSpreadBIFF5Writer.WriteFonts ()
*
* DESCRIPTION: Writes the Excel 5 FONT records neede for the
* used fonts in the workbook.
*
*******************************************************************}
procedure TsSpreadBiff5Writer.WriteFonts(AStream: TStream);
var
i: Integer;
begin
for i:=0 to Workbook.GetFontCount-1 do
WriteFont(AStream, Workbook.GetFont(i));
end;
{*******************************************************************
* TsSpreadBIFF5Writer.WriteRPNFormula ()
*
* DESCRIPTION: Writes an Excel 5 FORMULA record
*
@ -725,7 +715,7 @@ begin
AStream.WriteWord(WordToLE(ACol));
{ Index to XF Record }
AStream.WriteWord($0000);
WriteXFIndex(AStream, ACell);
{ Result of the formula in IEEE 754 floating-point value }
AStream.WriteBuffer(FormulaResult, 8);
@ -939,7 +929,7 @@ begin
AStream.WriteWord(WordToLE(ACol));
{ Index to XF record }
AStream.WriteWord(WordToLE(15));
WriteXFIndex(AStream, ACell);
{ Byte String with 16-bit size }
AStream.WriteWord(WordToLE(L));
@ -974,7 +964,7 @@ begin
AStream.WriteWord(WordToLE(ACol));
{ Index to XF record }
AStream.WriteWord($0);
WriteXFIndex(AStream, ACell);
{ IEE 754 floating-point value }
AStream.WriteBuffer(AValue, 8);
@ -1120,14 +1110,23 @@ end;
*
* DESCRIPTION: Writes an Excel 5 XF record
*
* Writes a number (64-bit floating point) to the sheet
*
*******************************************************************}
procedure TsSpreadBIFF5Writer.WriteXF(AStream: TStream; AFontIndex: Word;
AXF_TYPE_PROT: Byte);
AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders;
AHorAlignment: TsHorAlignment = haDefault; AVertAlignment: TsVertAlignment = vaDefault;
AWordWrap: Boolean = false; AddBackground: Boolean = false;
ABackgroundColor: TsColor = scSilver);
const
FILL_PATTERN = 1; // solid fill
BORDER_LINE_STYLE = 1; // thin solid line
BORDER_COLOR = scBLACK;
var
optns: Word;
b: Byte;
dw1, dw2: DWord;
XFOptions: Word;
XFAlignment, XFOrientationAttrib: Byte;
XFAlignment: byte;
XFBorderDWord1, XFBorderDWord2: DWord;
begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_XF));
@ -1137,43 +1136,263 @@ begin
AStream.WriteWord(WordToLE(AFontIndex));
{ Index to FORMAT record }
AStream.WriteWord($00);
AStream.WriteWord(WordToLE(AFormatIndex));
{ XF type, cell protection and parent style XF }
XFOptions := AXF_TYPE_PROT and MASK_XF_TYPE_PROT;
optns := AXF_TYPE_PROT and MASK_XF_TYPE_PROT;
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));
optns := optns or MASK_XF_TYPE_PROT_PARENT;
AStream.WriteWord(WordToLE(optns));
{ Alignment and text break }
XFAlignment := MASK_XF_VERT_ALIGN_BOTTOM;
b := 0;
case AHorAlignment of
haLeft : b := b or MASK_XF_HOR_ALIGN_LEFT;
haCenter : b := b or MASK_XF_HOR_ALIGN_CENTER;
haRight : b := b or MASK_XF_HOR_ALIGN_RIGHT;
end;
case AVertAlignment of
vaTop : b := b or MASK_XF_VERT_ALIGN_TOP;
vaCenter : b := b or MASK_XF_VERT_ALIGN_CENTER;
vaBottom : b := b or MASK_XF_VERT_ALIGN_BOTTOM;
else b := b or MASK_XF_VERT_ALIGN_BOTTOM;
end;
if AWordWrap then
b := b or MASK_XF_TEXTWRAP;
AStream.WriteByte(b);
AStream.WriteByte(WordToLE(XFAlignment));
{ Text orientation and flags for used attribute groups }
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(WordToLE(XFOrientationAttrib));
{ Text rotation }
AStream.WriteByte(ATextRotation); // 0 is horizontal / normal
{ Cell border lines and background area }
AStream.WriteDWord($000020C0);
AStream.WriteDWord($00000000);
dw1 := 0;
dw2 := 0;
// Background color
if AddBackground then begin
dw1 := dw1 or (ABackgroundColor and $0000007F);
dw1 := dw1 or (FILL_PATTERN shl 16);
end;
// Border lines
if cbSouth in ABorders then
dw1 := dw1 or (BORDER_LINE_STYLE shl 22);
dw1 := dw1 or (BORDER_COLOR shl 25); // Bottom line color
dw2 := (BORDER_COLOR shl 9) or // Top line color
(BORDER_COLOR shl 16) or // Left line color
(BORDER_COLOR shl 23); // Right line color
if cbNorth in ABorders then dw2 := dw2 or BORDER_LINE_STYLE;
if cbWest in ABorders then dw2 := dw2 or (BORDER_LINE_STYLE shl 3);
if cbEast in ABorders then dw2 := dw2 or (BORDER_LINE_STYLE shl 6);
AStream.WriteDWord(DWordToLE(dw1));
AStream.WriteDWord(DWordToLE(dw2));
end;
procedure TsSpreadBIFF5Writer.WriteXFFieldsForFormattingStyles(AStream: TStream);
var
i: Integer;
lFontIndex: Word;
lFormatIndex: Word; //number format
lTextRotation: Byte;
lBorders: TsCellBorders;
lAddBackground: Boolean;
lBackgroundColor: TsColor;
lHorAlign: TsHorAlignment;
lVertAlign: TsVertAlignment;
lWordWrap: Boolean;
fmt: String;
begin
// The first style was already added
for i := 1 to Length(FFormattingStyles) - 1 do begin
// Default styles
lFontIndex := 0;
lFormatIndex := 0; //General format (one of the built-in number formats)
lTextRotation := XF_ROTATION_HORIZONTAL;
lBorders := [];
lHorAlign := FFormattingStyles[i].HorAlignment;
lVertAlign := FFormattingStyles[i].VertAlignment;
lBackgroundColor := FFormattingStyles[i].BackgroundColor;
(*
// Now apply the modifications.
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then
case FFormattingStyles[i].NumberFormat of
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;
end;
*)
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_90DEG_CW;
rt90DegreeCounterClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CCW;
rtStacked : lTextRotation := XF_ROTATION_STACKED;
end;
end;
if uffBold in FFormattingStyles[i].UsedFormattingFields then
lFontIndex := 1; // must be before uffFont which overrides uffBold
// the "1" was defined in TsWorkbook.InitFont (FONT1)
if uffFont in FFormattingStyles[i].UsedFormattingFields then
lFontIndex := FFormattingStyles[i].FontIndex;
lAddBackground := (uffBackgroundColor in FFormattingStyles[i].UsedFormattingFields);
lWordwrap := (uffWordwrap in FFormattingStyles[i].UsedFormattingFields);
// And finally write the style
WriteXF(AStream, lFontIndex, lFormatIndex, 0, lTextRotation, lBorders,
lHorAlign, lVertAlign, lWordwrap, lAddBackground, lBackgroundColor);
end;
end;
{ Index to XF record, according to formatting }
procedure TsSpreadBIFF5Writer.WriteXFIndex(AStream: TStream; ACell: PCell);
var
lIndex: Integer;
lXFIndex: Word;
begin
// First try the fast methods for default formats
if ACell^.UsedFormattingFields = [] then
begin
AStream.WriteWord(WordToLE(15)); //XF15; see TsSpreadBIFF8Writer.AddDefaultFormats
Exit;
end;
(*
if ACell^.UsedFormattingFields = [uffTextRotation] then
begin
case ACell^.TextRotation of
rt90DegreeCounterClockwiseRotation: AStream.WriteWord(WordToLE(16)); //XF_16
rt90DegreeClockwiseRotation: AStream.WriteWord(WordToLE(17)); //XF_17
else
AStream.WriteWord(WordToLE(15)); //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?
else
AStream.WriteWord(WordToLE(15)); //e.g. nfGeneral: XF_15
end;
Exit;
end;
}
// 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('[TsSpreadBIFF5Writer.WriteXFIndex] Invalid Index, this should not happen!');
lXFIndex := FFormattingStyles[lIndex].Row;
AStream.WriteWord(WordToLE(lXFIndex));
end;
procedure TsSpreadBIFF5Writer.WriteXFRecords(AStream: TStream);
begin
// XF0
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF1
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF2
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF3
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF4
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF5
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF6
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF7
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF8
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF9
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF10
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF11
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF12
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF13
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF14
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []);
// XF15 - Default, no formatting
WriteXF(AStream, 0, 0, 0, XF_ROTATION_HORIZONTAL, []);
// Add all further non-standard/built-in formatting styles
ListAllFormattingStyles;
WriteXFFieldsForFormattingStyles(AStream);
end;
{*******************************************************************
* Initialization section
*
* Registers this reader / writer on fpSpreadsheet
*
*******************************************************************}
{ TsSpreadBIFF5Reader }
@ -1184,6 +1403,9 @@ var
RecordType: Word;
CurStreamPos: Int64;
begin
// Clear existing fonts. They will be replaced by those from the file.
FWorkbook.RemoveAllFonts;
while (not SectionEOF) do
begin
{ Read the record header }
@ -1193,9 +1415,12 @@ begin
CurStreamPos := AStream.Position;
case RecordType of
INT_EXCEL_ID_BOF: ;
INT_EXCEL_ID_BOUNDSHEET: ReadBoundSheet(AStream);
INT_EXCEL_ID_EOF: SectionEOF := True;
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_XF : ReadXF(AStream);
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
else
// nothing
end;
@ -1232,6 +1457,7 @@ begin
INT_EXCEL_ID_RSTRING: ReadRichString(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_RK: ReadRKValue(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_MULRK: ReadMulRKValues(AStream);
INT_EXCEL_ID_COLINFO: ReadColInfo(AStream);
INT_EXCEL_ID_ROWINFO: ReadRowInfo(AStream);
INT_EXCEL_ID_FORMULA: ReadFormulaExcel(AStream);
INT_EXCEL_ID_BOF: ;
@ -1437,17 +1663,6 @@ begin
Result:=Number;
end;
procedure TsSpreadBIFF5Reader.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;
procedure TsSpreadBIFF5Reader.ReadFromFile(AFileName: string; AData: TsWorkbook);
var
MemStream: TMemoryStream;
@ -1476,6 +1691,71 @@ begin
end;
end;
procedure TsSpreadBIFF5Reader.ReadXF(AStream: TStream);
type
TXFRecord = packed record // see p. 224
FontIndex: Word; // Offset 0, Size 2
FormatIndex: Word; // Offset 2, Size 2
XFType_CellProt_ParentStyleXF: Word; // Offset 4, Size 2
Align_TextBreak: Byte; // Offset 6, Size 1
XFRotation: Byte; // Offset 7, Size 1
Border_Background_1: DWord; // Offset 8, Size 4
Border_Background_2: DWord; // Offset 12, Size 4
end;
var
lData: TXFListData;
xf: TXFRecord;
b: Byte;
begin
AStream.ReadBuffer(xf, SizeOf(xf));
lData := TXFListData.Create;
// Font index
lData.FontIndex := WordLEToN(xf.FontIndex);
// Format index
lData.FormatIndex := WordLEToN(xf.FormatIndex);
// Horizontal text alignment
b := xf.Align_TextBreak AND MASK_XF_HOR_ALIGN;
if (b <= ord(High(TsHorAlignment))) then
lData.HorAlignment := TsHorAlignment(b)
else
lData.HorAlignment := haDefault;
// Vertical text alignment
b := (xf.Align_TextBreak AND MASK_XF_VERT_ALIGN) shr 4;
if (b + 1 <= ord(high(TsVertAlignment))) then
lData.VertAlignment := tsVertAlignment(b + 1) // + 1 due to vaDefault
else
lData.VertAlignment := vaDefault;
// Word wrap
lData.WordWrap := (xf.Align_TextBreak and MASK_XF_TEXTWRAP) <> 0;
// Cell borders and background
xf.Border_Background_1 := DWordLEToN(xf.Border_Background_1);
xf.Border_Background_2 := DWordLEToN(xf.Border_Background_2);
lData.Borders := [];
// the 4 masked bits encode the line style of the border line. 0 = no line
// We ignore the line style here. --> check against "no line"
if xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM <> 0 then
Include(lData.Borders, cbSouth);
if xf.Border_Background_2 and MASK_XF_BORDER_LEFT <> 0 then
Include(lData.Borders, cbWest);
if xf.Border_Background_2 and MASK_XF_BORDER_RIGHT <> 0 then
Include(lData.Borders, cbEast);
if xf.Border_Background_2 and MASK_XF_BORDER_TOP <> 0 then
Include(lData.Borders, cbNorth);
// Background color
lData.BackgroundColor := xf.Border_Background_1 AND MASK_XF_BKGR_PATTERN_COLOR;
// Add the XF to the list
FXFList.Add(lData);
end;
procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var
BIFF5EOF: Boolean;
@ -1525,6 +1805,68 @@ begin
ApplyCellFormatting(ARow, ACol, XF);
end;
procedure TsSpreadBIFF5Reader.ReadFont(const AStream: TStream);
var
lCodePage: Word;
lHeight: Word;
lOptions: Word;
lColor: Word;
lWeight: Word;
Len: Byte;
fontname: ansistring;
font: TsFont;
begin
font := TsFont.Create;
{ Height of the font in twips = 1/20 of a point }
lHeight := WordLEToN(AStream.ReadWord); // WordToLE(200)
font.Size := lHeight/20;
{ Option flags }
lOptions := WordLEToN(AStream.ReadWord);
font.Style := [];
if lOptions and $0001 <> 0 then Include(font.Style, fssBold);
if lOptions and $0002 <> 0 then Include(font.Style, fssItalic);
if lOptions and $0004 <> 0 then Include(font.Style, fssUnderline);
if lOptions and $0008 <> 0 then Include(font.Style, fssStrikeout);
{ Colour index }
lColor := WordLEToN(AStream.ReadWord);
//font.Color := TsColor(lColor - 8); // Palette colors have an offset 8
font.Color := tsColor(lColor);
{ Font weight }
lWeight := WordLEToN(AStream.ReadWord);
if lWeight = 700 then Include(font.Style, fssBold);
{ Escapement type }
AStream.ReadWord();
{ Underline type }
if AStream.ReadByte > 0 then Include(font.Style, fssUnderline);
{ Font family }
AStream.ReadByte();
{ Character set }
lCodepage := AStream.ReadByte();
{$ifdef FPSPREADDEBUG}
WriteLn('Reading Font Codepage='+IntToStr(lCodepage));
{$endif}
{ Not used }
AStream.ReadByte();
{ Font name: Ansistring, char count in 1 byte }
Len := AStream.ReadByte();
SetLength(fontname, Len);
AStream.ReadBuffer(fontname[1], Len);
font.FontName := fontname;
{ Add font to workbook's font list }
FWorkbook.AddFont(font);
end;
procedure TsSpreadBIFF5Reader.ReadFormula(AStream: TStream);
begin
@ -1569,12 +1911,6 @@ begin
ApplyCellFormatting(ARow, ACol, XF);
end;
procedure TsSpreadBIFF5Reader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Integer);
begin
// to do...
end;
initialization

View File

@ -63,29 +63,6 @@ uses
fpsutils, lazutf8;
type
TXFRecordData = class
public
FontIndex: Integer;
FormatIndex: Integer;
HorAlignment: TsHorAlignment;
VertAlignment: TsVertAlignment;
WordWrap: Boolean;
Borders: TsCellBorders;
BackgroundColor: TsColor;
{
FontIndex: Integer;
Border: TsBorder;
XFType_Protection: Word;
Align_TextBreak: Byte;
XF_Rotation: Byte;
Indent_Shrink_TextDir: Byte;
UsedAttrib: Byte;
Border_Background1: DWord;
Border_Background2: DWord;
Border_Background3: Word;
}
end;
TFormatRecordData = class
public
Index: Integer;
@ -101,10 +78,9 @@ type
FWorksheetNames: TStringList;
FCurrentWorksheet: Integer;
FSharedStringTable: TStringList;
FXFList: TFPList; // of TXFRecordData
FFormatList: TFPList; // of TFormatRecordData
function DecodeRKValue(const ARK: DWORD): Double;
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer);
// procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer);
procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Word;
out ANumberFormatStr: String);
@ -119,9 +95,8 @@ type
procedure ReadRKValue(const AStream: TStream);
procedure ReadMulRKValues(const AStream: TStream);
procedure ReadRowColXF(const AStream: TStream; out ARow,ACol,AXF: WORD);
// 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);
// Read XF record
@ -135,13 +110,12 @@ type
// procedure ReadCodepage in xlscommon
// procedure ReadDateMode in xlscommon
procedure ReadFont(const AStream: TStream);
// Read col info
procedure ReadColInfo(const AStream: TStream);
{ Record reading methods }
procedure ReadBlank(AStream: TStream); override;
procedure ReadFormula(AStream: TStream); override;
procedure ReadLabel(AStream: TStream); override;
procedure ReadNumber(AStream: TStream); override;
procedure ReadRichString(const AStream: TStream);
public
constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
@ -164,12 +138,8 @@ type
ACell: PCell); override;
procedure WriteBOF(AStream: TStream; ADataType: Word);
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
// procedure WriteCodepage in xlscommon; Workbook Globals record
procedure WriteColInfo(AStream: TStream; ACol: PCol);
procedure WriteColInfos(AStream: TStream; ASheet: TsWorksheet);
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override;
// procedure WriteDateMode in xlscommon; Workbook Globals record
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TsFont);
@ -279,7 +249,6 @@ const
INT_EXCEL_ID_BLANK = $0201;
INT_EXCEL_ID_BOF = $0809;
INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs
INT_EXCEL_ID_COLINFO = $007D;
INT_EXCEL_ID_COUNTRY = $008C;
INT_EXCEL_ID_EOF = $000A;
INT_EXCEL_ID_DIMENSIONS = $0200;
@ -291,7 +260,6 @@ const
INT_EXCEL_ID_STYLE = $0293;
INT_EXCEL_ID_WINDOW1 = $003D;
INT_EXCEL_ID_WINDOW2 = $023E;
INT_EXCEL_ID_XF = $00E0;
INT_EXCEL_ID_RSTRING = $00D6;
INT_EXCEL_ID_RK = $027E;
INT_EXCEL_ID_MULRK = $00BD;
@ -318,10 +286,6 @@ const
INT_BOF_BUILD_ID = $1FD2;
INT_BOF_BUILD_YEAR = $07CD;
{ FONT record constants }
INT_FONT_WEIGHT_NORMAL = $0190;
INT_FONT_WEIGHT_BOLD = $02BC;
{ FORMULA record constants }
MASK_FORMULA_RECALCULATE_ALWAYS = $0001;
MASK_FORMULA_RECALCULATE_ON_OPEN = $0002;
@ -352,44 +316,11 @@ const
{ 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 HORIZONTAL ALIGN }
MASK_XF_HOR_ALIGN_LEFT = $01;
MASK_XF_HOR_ALIGN_CENTER = $02;
MASK_XF_HOR_ALIGN_RIGHT = $03;
MASK_XF_HOR_ALIGN_FILLED = $04;
MASK_XF_HOR_ALIGN_JUSTIFIED = $05; // BIFF4-BIFF8
MASK_XF_HOR_ALIGN_CENTERED_SELECTION= $06; // BIFF4-BIFF8
MASK_XF_HOR_ALIGN_DISTRIBUTED = $07; // BIFF8
{ XF_VERT_ALIGN }
MASK_XF_VERT_ALIGN_TOP = $00;
MASK_XF_VERT_ALIGN_CENTER = $10;
MASK_XF_VERT_ALIGN_BOTTOM = $20;
MASK_XF_VERT_ALIGN_JUSTIFIED = $30;
{ XF_ROTATION }
XF_ROTATION_HORIZONTAL = 0;
XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE = 90;
XF_ROTATION_90_DEGREE_CLOCKWISE = 180;
XF_ROTATION_HORIZONTAL = 0;
XF_ROTATION_90DEG_CCW = 90;
XF_ROTATION_90DEG_CW = 180;
XF_ROTATION_STACKED = 255; // Letters stacked top to bottom, but not rotated
{ XF CELL BORDER }
MASK_XF_BORDER_LEFT = $0000000F;
@ -397,18 +328,6 @@ const
MASK_XF_BORDER_TOP = $00000F00;
MASK_XF_BORDER_BOTTOM = $0000F000;
{ XF record constants }
MASK_XF_TYPE_PROT = $0007;
MASK_XF_TYPE_PROT_PARENT = $FFF0;
MASK_XF_HOR_ALIGN = $07;
MASK_XF_VERT_ALIGN = $70;
MASK_XF_TEXTWRAP = $08;
{
Exported functions
}
{ TsSpreadBIFF8Writer }
@ -425,6 +344,7 @@ begin
Exit;
end;
{
if ACell^.UsedFormattingFields = [uffTextRotation] then
begin
case ACell^.TextRotation of
@ -435,6 +355,7 @@ begin
end;
Exit;
end;
}
{
uffNumberFormat does not seem to have default XF indexes, but perhaps look at XF_21
@ -553,8 +474,9 @@ begin
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;
rt90DegreeClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CW;
rt90DegreeCounterClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CCW;
rtStacked: lTextRotation := XF_ROTATION_STACKED;
end;
end;
@ -680,17 +602,11 @@ begin
AStream.Position := CurrentPos;
WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream);
WriteColInfos(AStream, sheet);
WriteDimensions(AStream, sheet);
WriteWindow2(AStream, True);
WriteCellsToStream(AStream, sheet.Cells);
WriteIndex(AStream);
WriteColInfos(AStream, sheet);
WriteDimensions(AStream, sheet);
WriteWindow2(AStream, True);
WriteCellsToStream(AStream, sheet.Cells);
WriteEOF(AStream);
end;
@ -800,42 +716,6 @@ begin
AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar));
end;
{*******************************************************************
* TsSpreadBIFF8Writer.WriteColInfo ()
*
* DESCRIPTION: Writes a COLINFO record
*******************************************************************}
procedure TsSpreadBIFF8Writer.WriteColInfo(AStream: TStream; ACol: PCol);
var
w: Integer;
begin
if Assigned(ACol) then begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLINFO)); // BIFF record header
AStream.WriteWord(WordToLE(12)); // Record size
AStream.WriteWord(WordToLE(ACol^.Col)); // start column
AStream.WriteWord(WordToLE(ACol^.Col)); // end column
{ calculate width to be in units of 1/256 of pixel width of character "0" }
w := round(ACol^.Width * 256);
AStream.WriteWord(WordToLE(w)); // write width
AStream.WriteWord(15); // XF record, ignored
AStream.WriteWord(0); // option flags, ignored
AStream.WriteWord(0); // "not used"
end;
end;
procedure TsSpreadBIFF8Writer.WriteColInfos(AStream: TStream;
ASheet: TsWorksheet);
var
j: Integer;
col: PCol;
begin
for j := 0 to ASheet.Cols.Count-1 do begin
col := PCol(ASheet.Cols[j]);
WriteColInfo(AStream, col);
end;
end;
{*******************************************************************
* TsSpreadBIFF8Writer.WriteDateTime ()
*
@ -949,7 +829,6 @@ begin
AStream.WriteWord(WordToLE(optn));
{ Colour index }
//AStream.WriteWord(WordToLE(8 + ord(AFont.Color))); //WordToLE($7FFF));
AStream.WriteWord(WordToLE(ord(AFont.Color)));
{ Font weight }
@ -1643,6 +1522,22 @@ end;
{ TsSpreadBIFF8Reader }
constructor TsSpreadBIFF8Reader.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
FFormatList := TFPList.Create;
end;
destructor TsSpreadBIFF8Reader.Destroy;
var
j: integer;
begin
for j := FFormatList.Count-1 downto 0 do TObject(FFormatList[j]).Free;
FFormatList.Free;
if Assigned(FSharedStringTable) then FSharedStringTable.Free;
inherited;
end;
function TsSpreadBIFF8Reader.DecodeRKValue(const ARK: DWORD): Double;
var
Number: Double;
@ -1701,7 +1596,7 @@ const
0, 0, 0, 0, 0, 0, 0, 0); // 51..58
var
lFormatData: TFormatRecordData;
lXFData: TXFRecordData;
lXFData: TXFListData;
isAMPM: Boolean;
isLongTime: Boolean;
isMilliSec: Boolean;
@ -1719,7 +1614,7 @@ begin
if lFormatData = nil then begin
// no custom format, so first test for default formats
lXFData := TXFRecordData (FXFList.Items[AXFIndex]);
lXFData := TXFListData (FXFList.Items[AXFIndex]);
case lXFData.FormatIndex of
FORMAT_DATE_DM:
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end;
@ -1861,7 +1756,7 @@ begin
Result := DecomprStrValue;
end;
if StringFlags and 8 = 8 then begin
//Rich string (This only happend in BIFF8)
//Rich string (This only happened in BIFF8)
for j := 1 to RunsCounter do begin
if (PendingRecordSize<=0) then begin
//A CONTINUE may happend here
@ -1909,14 +1804,13 @@ var
begin
// Clear existing fonts. They will be replaced by those from the file.
FWorkbook.RemoveAllFonts;
if Assigned(FSharedStringTable) then FreeAndNil(FSharedStringTable);
while (not SectionEOF) do
begin
while (not SectionEOF) do begin
{ Read the record header }
RecordType := WordLEToN(AStream.ReadWord);
RecordSize := WordLEToN(AStream.ReadWord);
PendingRecordSize:=RecordSize;
PendingRecordSize := RecordSize;
CurStreamPos := AStream.Position;
@ -2086,86 +1980,12 @@ begin
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;
{ Applies the XF formatting given by the given index to the specified cell }
procedure TsSpreadBIFF8Reader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Integer);
var
lCell: PCell;
XFData: TXFRecordData;
begin
lCell := FWorksheet.GetCell(ARow, ACol);
if Assigned(lCell) then begin
XFData := TXFRecordData(FXFList.Items[XFIndex]);
// Font
if XFData.FontIndex = 1 then
Include(lCell^.UsedFormattingFields, uffBold)
else
if XFData.FontIndex > 0 then
Include(lCell^.UsedFormattingFields, uffFont);
lCell^.FontIndex := XFData.FontIndex;
// Alignment
lCell^.HorAlignment := XFData.HorAlignment;
lCell^.VertAlignment := XFData.VertAlignment;
// Word wrap
if XFData.WordWrap then
Include(lCell^.UsedFormattingFields, uffWordWrap)
else
Exclude(lCell^.UsedFormattingFields, uffWordWrap);
// Borders
if XFData.Borders <> [] then begin
Include(lCell^.UsedFormattingFields, uffBorder);
lCell^.Border := XFData.Borders;
end else
Exclude(lCell^.UsedFormattingFields, uffBorder);
// Background color
if XFData.BackgroundColor <> 0 then begin
Include(lCell^.UsedFormattingFields, uffBackgroundColor);
lCell^.BackgroundColor := XFData.BackgroundColor;
end;
end;
end;
function TsSpreadBIFF8Reader.ReadString(const AStream: TStream;
const ALength: WORD): UTF8String;
begin
Result:=UTF16ToUTF8(ReadWideString(AStream, ALength));
end;
constructor TsSpreadBIFF8Reader.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
FXFList := TFPList.Create;
FFormatList := TFPList.Create;
end;
destructor TsSpreadBIFF8Reader.Destroy;
var
j: integer;
begin
for j := FXFList.Count-1 downto 0 do TObject(FXFList[j]).Free;
for j := FFormatList.Count-1 downto 0 do TObject(FFormatList[j]).Free;
FXFList.Free;
FFormatList.Free;
if Assigned(FSharedStringTable) then FSharedStringTable.Free;
inherited;
end;
procedure TsSpreadBIFF8Reader.ReadFromFile(AFileName: string; AData: TsWorkbook);
var
MemStream: TMemoryStream;
@ -2466,13 +2286,13 @@ type
Border_Background_3: DWord; // Offset 18, Size 2
end;
var
lData: TXFRecordData;
lData: TXFListData;
xf: TXFRecord;
b: Byte;
begin
AStream.ReadBuffer(xf, SizeOf(xf));
lData := TXFRecordData.Create;
lData := TXFListData.Create;
// Font index
lData.FontIndex := WordLEToN(xf.FontIndex);
@ -2541,12 +2361,12 @@ end;
function TsSpreadBIFF8Reader.FindFormatRecordForCell(const AXFIndex: Integer
): TFormatRecordData;
var
lXFData: TXFRecordData;
lXFData: TXFListData;
lFormatData: TFormatRecordData;
i: Integer;
begin
Result := nil;
lXFData := TXFRecordData(FXFList.Items[AXFIndex]);
lXFData := TXFListData(FXFList.Items[AXFIndex]);
for i := 0 to FFormatList.Count-1 do
begin
lFormatData := TFormatRecordData(FFormatList.Items[i]);
@ -2614,24 +2434,6 @@ begin
FWorkbook.AddFont(font);
end;
procedure TsSpreadBiff8Reader.ReadColInfo(const AStream: TStream);
var
c, c1, c2: Cardinal;
w: Word;
col: TCol;
begin
// read column start and end index of column range
c1 := WordLEToN(AStream.ReadWord);
c2 := WordLEToN(AStream.ReadWord);
// read col width in 1/256 of the width of "0" character
w := WordLEToN(AStream.ReadWord);
// calculate width in units of "characters"
col.Width := w / 256;
// assign width to columns
for c := c1 to c2 do
FWorksheet.WriteColInfo(c, col);
end;
{*******************************************************************
* Initialization section

View File

@ -18,8 +18,14 @@ const
{ RECORD IDs which didn't change across versions 2-8 }
INT_EXCEL_ID_FONT = $0031;
INT_EXCEL_ID_CODEPAGE = $0042;
INT_EXCEL_ID_COLINFO = $007D;
INT_EXCEL_ID_DATEMODE = $0022;
INT_EXCEL_ID_PALETTE = $0092;
INT_EXCEL_ID_XF = $00E0;
{ FONT record constants }
INT_FONT_WEIGHT_NORMAL = $0190;
INT_FONT_WEIGHT_BOLD = $02BC;
{ Formula constants TokenID values }
@ -183,46 +189,6 @@ const
// 1AH tSheet Start of external sheet reference (BIFF2-BIFF4)
// 1BH tEndSheet End of external sheet reference (BIFF2-BIFF4)
{ Built In Color Palette Indexes }
// Proper spelling
BUILT_IN_COLOR_PALETTE_BLACK = $08; // 000000H
BUILT_IN_COLOR_PALETTE_WHITE = $09; // FFFFFFH
BUILT_IN_COLOR_PALETTE_RED = $0A; // FF0000H
BUILT_IN_COLOR_PALETTE_GREEN = $0B; // 00FF00H
BUILT_IN_COLOR_PALETTE_BLUE = $0C; // 0000FFH
BUILT_IN_COLOR_PALETTE_YELLOW = $0D; // FFFF00H
BUILT_IN_COLOR_PALETTE_MAGENTA = $0E; // FF00FFH
BUILT_IN_COLOR_PALETTE_CYAN = $0F; // 00FFFFH
BUILT_IN_COLOR_PALETTE_DARK_RED = $10; // 800000H
BUILT_IN_COLOR_PALETTE_DARK_GREEN= $11; // 008000H
BUILT_IN_COLOR_PALETTE_DARK_BLUE = $12; // 000080H
BUILT_IN_COLOR_PALETTE_OLIVE = $13; // 808000H
BUILT_IN_COLOR_PALETTE_PURPLE = $14; // 800080H
BUILT_IN_COLOR_PALETTE_TEAL = $15; // 008080H
BUILT_IN_COLOR_PALETTE_SILVER = $16; // C0C0C0H
BUILT_IN_COLOR_PALETTE_GREY = $17; // 808080H
// Spelling mistake; kept for compatibility
BUILT_IN_COLOR_PALLETE_BLACK = $08 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_WHITE = $09 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_RED = $0A deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_GREEN = $0B deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_BLUE = $0C deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_YELLOW = $0D deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_MAGENTA = $0E deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_CYAN = $0F deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_DARK_RED = $10 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_DARK_GREEN= $11 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_DARK_BLUE = $12 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_OLIVE = $13 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_PURPLE = $14 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_TEAL = $15 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_SILVER = $16 deprecated 'Please use the *_PALETTE version';
BUILT_IN_COLOR_PALLETE_GREY = $17 deprecated 'Please use the *_PALETTE version';
EXTRA_COLOR_PALETTE_GREY10PCT = $18; // E6E6E6H //todo: is $18 correct? see 5.74.3 Built-In Default Colour Tables
EXTRA_COLOR_PALETTE_GREY20PCT = $19; // CCCCCCH //todo: is $19 correct? see 5.74.3 Built-In Default Colour Tables
{ CODEPAGE record constants }
WORD_ASCII = 367;
WORD_UTF_16 = 1200; // BIFF 8
@ -267,6 +233,57 @@ const
FORMAT_TIME_MSZ = 47; //time MM:SS.0
FORMAT_TIME_INTERVAL = 46; //time [hh]:mm:ss, hh can be >24
{ 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 = $01;
MASK_XF_USED_ATTRIB_FONT = $02;
MASK_XF_USED_ATTRIB_TEXT = $04;
MASK_XF_USED_ATTRIB_BORDER_LINES = $08;
MASK_XF_USED_ATTRIB_BACKGROUND = $10;
MASK_XF_USED_ATTRIB_CELL_PROTECTION = $20;
{ the following values do not agree with the documentation !!!
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 record constants }
MASK_XF_TYPE_PROT = $0007;
MASK_XF_TYPE_PROT_PARENT = $FFF0;
MASK_XF_HOR_ALIGN = $07;
MASK_XF_VERT_ALIGN = $70;
MASK_XF_TEXTWRAP = $08;
{ XF HORIZONTAL ALIGN }
MASK_XF_HOR_ALIGN_LEFT = $01;
MASK_XF_HOR_ALIGN_CENTER = $02;
MASK_XF_HOR_ALIGN_RIGHT = $03;
MASK_XF_HOR_ALIGN_FILLED = $04;
MASK_XF_HOR_ALIGN_JUSTIFIED = $05; // BIFF4-BIFF8
MASK_XF_HOR_ALIGN_CENTERED_SELECTION = $06; // BIFF4-BIFF8
MASK_XF_HOR_ALIGN_DISTRIBUTED = $07; // BIFF8
{ XF_VERT_ALIGN }
MASK_XF_VERT_ALIGN_TOP = $00;
MASK_XF_VERT_ALIGN_CENTER = $10;
MASK_XF_VERT_ALIGN_BOTTOM = $20;
MASK_XF_VERT_ALIGN_JUSTIFIED = $30;
type
TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28
@ -280,25 +297,41 @@ type
(const ADateTime: TDateTime; ADateMode: TDateMode): Double;
type
{ Contents of the XF record to be stored in the XFList of the reader }
TXFListData = class
public
FontIndex: Integer;
FormatIndex: Integer;
HorAlignment: TsHorAlignment;
VertAlignment: TsVertAlignment;
WordWrap: Boolean;
Borders: TsCellBorders;
BackgroundColor: TsColor;
end;
{ TsSpreadBIFFReader }
TsSpreadBIFFReader = class(TsCustomSpreadReader)
protected
FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding
FDateMode: TDateMode;
FPaletteFound: Boolean;
// converts an Excel color index to a color value.
// function ExcelPaletteToFPSColor(AIndex: Word): TsColor;
// Here we can add reading of records which didn't change across BIFF2-8 versions
// Workbook Globals records
FXFList: TFPList;
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer); virtual;
// Here we can add reading of records which didn't change across BIFF5-8 versions
procedure ReadCodePage(AStream: TStream);
// Read column info
procedure ReadColInfo(const AStream: TStream);
// Figures out what the base year for dates is for this file
procedure ReadDateMode(AStream: TStream);
// Read palette
procedure ReadPalette(AStream: TStream);
// Read the row, column, and XF index at the current stream position
procedure ReadRowColXF(AStream: TStream; out ARow, ACol, AXF: Word);
// Read row info
procedure ReadRowInfo(AStream: TStream); virtual;
public
constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
end;
{ TsSpreadBIFFWriter }
@ -308,16 +341,17 @@ type
FDateMode: TDateMode;
FLastRow: Integer;
FLastCol: Word;
// function FPSColorToExcelPalette(AColor: TsColor): Word;
procedure GetLastRowCallback(ACell: PCell; AStream: TStream);
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
function FormulaElementKindToExcelTokenID(AElementKind: TFEKind; out ASecondaryID: Word): Word;
// Other records which didn't change
// Workbook Globals records
// Write out used codepage for character encoding
procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding);
// Writes out column info(s)
procedure WriteColInfo(AStream: TStream; ACol: PCol);
procedure WriteColInfos(AStream: TStream; ASheet: TsWorksheet);
// Writes out DATEMODE record depending on FDateMode
procedure WriteDateMode(AStream: TStream);
// Writes out a PALETTE record containing all colors defined in the workbook
@ -387,40 +421,70 @@ begin
end;
end;
{ TsSpreadBIFFReader }
constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
FXFList := TFPList.Create;
// Initial base date in case it won't be read from file
FDateMode := dm1900;
end;
(*
function TsSpreadBIFFReader.ExcelPaletteToFPSColor(AIndex: Word): TsColor;
destructor TsSpreadBIFFReader.Destroy;
var
j: integer;
begin
case AIndex of
BUILT_IN_COLOR_PALLETE_BLACK : Result := scBlack;
BUILT_IN_COLOR_PALLETE_WHITE: Result := scWhite;
BUILT_IN_COLOR_PALLETE_RED: Result := scRed;
BUILT_IN_COLOR_PALLETE_GREEN: Result := scGreen;
BUILT_IN_COLOR_PALLETE_BLUE: Result := scBlue;
BUILT_IN_COLOR_PALLETE_YELLOW: Result := scYellow;
BUILT_IN_COLOR_PALLETE_MAGENTA: Result := scMagenta;
BUILT_IN_COLOR_PALLETE_CYAN: Result := scCyan;
BUILT_IN_COLOR_PALLETE_DARK_RED: Result := scDarkRed;
BUILT_IN_COLOR_PALLETE_DARK_GREEN: Result := scDarkGreen;
BUILT_IN_COLOR_PALLETE_DARK_BLUE: Result := scDarkBlue;
BUILT_IN_COLOR_PALLETE_OLIVE: Result := scOlive;
BUILT_IN_COLOR_PALLETE_PURPLE: Result := scPurple;
BUILT_IN_COLOR_PALLETE_TEAL: Result := scTeal;
BUILT_IN_COLOR_PALLETE_SILVER: Result := scSilver;
BUILT_IN_COLOR_PALLETE_GREY: Result := scGrey;
//
EXTRA_COLOR_PALETTE_GREY10PCT: Result := scGrey10pct;
EXTRA_COLOR_PALETTE_GREY20PCT: Result := scGrey20pct;
for j := FXFList.Count-1 downto 0 do TObject(FXFList[j]).Free;
FXFList.Free;
inherited Destroy;
end;
{ Applies the XF formatting given by the given index to the specified cell }
procedure TsSpreadBIFFReader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Integer);
var
lCell: PCell;
XFData: TXFListData;
begin
lCell := FWorksheet.GetCell(ARow, ACol);
if Assigned(lCell) then begin
XFData := TXFListData(FXFList.Items[XFIndex]);
// Font
if XFData.FontIndex = 1 then
Include(lCell^.UsedFormattingFields, uffBold)
else
if XFData.FontIndex > 1 then
Include(lCell^.UsedFormattingFields, uffFont);
lCell^.FontIndex := XFData.FontIndex;
// Alignment
lCell^.HorAlignment := XFData.HorAlignment;
lCell^.VertAlignment := XFData.VertAlignment;
// Word wrap
if XFData.WordWrap then
Include(lCell^.UsedFormattingFields, uffWordWrap)
else
Exclude(lCell^.UsedFormattingFields, uffWordWrap);
// Borders
if XFData.Borders <> [] then begin
Include(lCell^.UsedFormattingFields, uffBorder);
lCell^.Border := XFData.Borders;
end else
Exclude(lCell^.UsedFormattingFields, uffBorder);
// Background color
if XFData.BackgroundColor <> 0 then begin
Include(lCell^.UsedFormattingFields, uffBackgroundColor);
lCell^.BackgroundColor := XFData.BackgroundColor;
end;
end;
end;
*)
// In BIFF 8 it seams to always use the UTF-16 codepage
procedure TsSpreadBIFFReader.ReadCodePage(AStream: TStream);
var
@ -476,6 +540,24 @@ begin
end;
end;
procedure TsSpreadBiffReader.ReadColInfo(const AStream: TStream);
var
c, c1, c2: Cardinal;
w: Word;
col: TCol;
begin
// read column start and end index of column range
c1 := WordLEToN(AStream.ReadWord);
c2 := WordLEToN(AStream.ReadWord);
// read col width in 1/256 of the width of "0" character
w := WordLEToN(AStream.ReadWord);
// calculate width in units of "characters"
col.Width := w / 256;
// assign width to columns
for c := c1 to c2 do
FWorksheet.WriteColInfo(c, col);
end;
procedure TsSpreadBIFFReader.ReadDateMode(AStream: TStream);
var
lBaseMode: Word;
@ -514,6 +596,18 @@ begin
FPaletteFound := true;
end;
// Read the row, column and xf index
procedure TsSpreadBIFFReader.ReadRowColXF(AStream: TStream; out ARow,
ACol, AXF: WORD);
begin
{ BIFF Record data for row and column}
ARow := WordLEToN(AStream.ReadWord);
ACol := WordLEToN(AStream.ReadWord);
{ Index to XF record }
AXF := WordLEtoN(AStream.ReadWord);
end;
// Read the part of the ROW record that is common to all BIFF versions
procedure TsSpreadBIFFReader.ReadRowInfo(AStream: TStream);
type
@ -768,6 +862,37 @@ begin
AStream.WriteWord(WordToLE(lCodepage));
end;
procedure TsSpreadBIFFWriter.WriteColInfo(AStream: TStream; ACol: PCol);
var
w: Integer;
begin
if Assigned(ACol) then begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLINFO)); // BIFF record header
AStream.WriteWord(WordToLE(12)); // Record size
AStream.WriteWord(WordToLE(ACol^.Col)); // start column
AStream.WriteWord(WordToLE(ACol^.Col)); // end column
{ calculate width to be in units of 1/256 of pixel width of character "0" }
w := round(ACol^.Width * 256);
AStream.WriteWord(WordToLE(w)); // write width
AStream.WriteWord(15); // XF record, ignored
AStream.WriteWord(0); // option flags, ignored
AStream.WriteWord(0); // "not used"
end;
end;
procedure TsSpreadBIFFWriter.WriteColInfos(AStream: TStream;
ASheet: TsWorksheet);
var
j: Integer;
col: PCol;
begin
for j := 0 to ASheet.Cols.Count-1 do begin
col := PCol(ASheet.Cols[j]);
WriteColInfo(AStream, col);
end;
end;
procedure TsSpreadBIFFWriter.WriteDateMode(AStream: TStream);
begin
{ BIFF Record header }