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

View File

@ -31,6 +31,18 @@ type
{ BIFF2 file format tests } { BIFF2 file format tests }
procedure TestWriteReadBIFF2_Font_InternalPal; // internal palette for BIFF2 file format 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 } { BIFF8 file format tests }
// Background colors... // Background colors...
procedure TestWriteReadBIFF8_Background_InternalPal; // internal palette procedure TestWriteReadBIFF8_Background_InternalPal; // internal palette
@ -250,6 +262,47 @@ begin
TestWriteReadFontColors(sfExcel2, 0); TestWriteReadFontColors(sfExcel2, 0);
end; 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 } { Tests for BIFF8 file format }
procedure TSpreadWriteReadColorTests.TestWriteReadBIFF8_Background_InternalPal; procedure TSpreadWriteReadColorTests.TestWriteReadBIFF8_Background_InternalPal;
begin begin

View File

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

View File

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

View File

@ -78,15 +78,14 @@ type
TsSpreadBIFF5Reader = class(TsSpreadBIFFReader) TsSpreadBIFF5Reader = class(TsSpreadBIFFReader)
private private
RecordSize: Word; RecordSize: Word;
FWorksheet: TsWorksheet;
FWorksheetNames: TStringList; FWorksheetNames: TStringList;
FCurrentWorksheet: Integer; FCurrentWorksheet: Integer;
protected protected
{ Helpers } { Helpers }
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer);
function DecodeRKValue(const ARK: DWORD): Double; function DecodeRKValue(const ARK: DWORD): Double;
{ Record writing methods } { Record writing methods }
procedure ReadBlank(AStream: TStream); override; procedure ReadBlank(AStream: TStream); override;
procedure ReadFont(const AStream: TStream);
procedure ReadFormula(AStream: TStream); override; procedure ReadFormula(AStream: TStream); override;
procedure ReadFormulaExcel(AStream: TStream); procedure ReadFormulaExcel(AStream: TStream);
procedure ReadLabel(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override;
@ -97,7 +96,7 @@ type
procedure ReadBoundsheet(AStream: TStream); procedure ReadBoundsheet(AStream: TStream);
procedure ReadRichString(AStream: TStream); procedure ReadRichString(AStream: TStream);
procedure ReadRKValue(AStream: TStream); procedure ReadRKValue(AStream: TStream);
procedure ReadRowColXF(const AStream: TStream; out ARow,ACol,AXF: WORD); virtual; procedure ReadXF(AStream: TStream);
public public
{ General reading methods } { General reading methods }
procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override; procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override;
@ -110,28 +109,36 @@ type
private private
WorkBookEncoding: TsEncoding; WorkBookEncoding: TsEncoding;
protected protected
procedure AddDefaultFormats; override;
{ Record writing methods } { Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
procedure WriteBOF(AStream: TStream; ADataType: Word); procedure WriteBOF(AStream: TStream; ADataType: Word);
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; 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; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override; const AValue: TDateTime; ACell: PCell); override;
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteEOF(AStream: TStream); procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TFPCustomFont); procedure WriteFont(AStream: TStream; AFont: TsFont);
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteFonts(AStream: TStream);
const AFormula: TsRPNFormula; ACell: PCell); override;
procedure WriteIndex(AStream: TStream); procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: string; ACell: PCell); override; const AValue: string; ACell: PCell); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: double; ACell: PCell); override; 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 WriteStyle(AStream: TStream);
procedure WriteWindow1(AStream: TStream); procedure WriteWindow1(AStream: TStream);
procedure WriteWindow2(AStream: TStream; ASheetSelected: Boolean); 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 public
{ General writing methods } { General writing methods }
procedure WriteToFile(const AFileName: string; procedure WriteToFile(const AFileName: string;
@ -307,43 +314,40 @@ const
{ XF substructures } { XF substructures }
{ XF_TYPE_PROT - XF Type and Cell protection (3 Bits) - BIFF3-BIFF8 } { XF substructures --- see xlscommon! }
MASK_XF_TYPE_PROT_LOCKED = $1; XF_ROTATION_HORIZONTAL = 0;
MASK_XF_TYPE_PROT_FORMULA_HIDDEN = $2; XF_ROTATION_STACKED = 1;
MASK_XF_TYPE_PROT_STYLE_XF = $4; // 0 = CELL XF XF_ROTATION_90DEG_CCW = 2;
XF_ROTATION_90DEG_CW = 3;
{ XF_USED_ATTRIB - Attributes from parent Style XF (6 Bits) - BIFF3-BIFF8 { XF CELL BORDER }
MASK_XF_BORDER_LEFT = $00000038;
In a CELL XF a cleared bit means that the parent attribute is used, MASK_XF_BORDER_RIGHT = $000001C0;
while a set bit indicates that the data in this XF is used 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, { XF CELL BACKGROUND }
while a set bit indicates that the attribute should be ignored } MASK_XF_BKGR_PATTERN_COLOR = $0000007F;
MASK_XF_USED_ATTRIB_NUMBER_FORMAT = $04; MASK_XF_BKGR_BACKGROUND_COLOR = $00003F80;
MASK_XF_USED_ATTRIB_FONT = $08; MASK_XF_BKGR_FILLPATTERN = $003F0000;
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;
{ XF record constants }
MASK_XF_TYPE_PROT = $0007;
MASK_XF_TYPE_PROT_PARENT = $FFF0;
MASK_XF_VERT_ALIGN = $70;
{
Exported functions
}
{ TsSpreadBIFF5Writer } { 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 () * TsSpreadBIFF5Writer.WriteToFile ()
* *
@ -392,11 +396,10 @@ end;
*******************************************************************} *******************************************************************}
procedure TsSpreadBIFF5Writer.WriteToStream(AStream: TStream); procedure TsSpreadBIFF5Writer.WriteToStream(AStream: TStream);
var var
FontData: TFPCustomFont;
MyData: TMemoryStream;
CurrentPos: Int64; CurrentPos: Int64;
Boundsheets: array of Int64; Boundsheets: array of Int64;
i, len: Integer; i, len: Integer;
sheet : TsWorksheet;
begin begin
{ Store some data about the workbook that other routines need } { Store some data about the workbook that other routines need }
WorkBookEncoding := Workbook.Encoding; WorkBookEncoding := Workbook.Encoding;
@ -404,60 +407,12 @@ begin
{ Write workbook globals } { Write workbook globals }
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
WriteCodepage(AStream, WorkBookEncoding); WriteCodepage(AStream, WorkBookEncoding);
WriteWindow1(AStream); WriteWindow1(AStream);
WriteFonts(AStream);
FontData := TFPCustomFont.Create; WritePalette(AStream);
try WriteXFRecords(AStream);
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);
WriteStyle(AStream); WriteStyle(AStream);
// A BOUNDSHEET for each worksheet // A BOUNDSHEET for each worksheet
@ -474,6 +429,8 @@ begin
for i := 0 to Workbook.GetWorksheetCount - 1 do for i := 0 to Workbook.GetWorksheetCount - 1 do
begin begin
sheet := Workbook.GetWorksheetByIndex(i);
{ First goes back and writes the position of the BOF of the { First goes back and writes the position of the BOF of the
sheet on the respective BOUNDSHEET record } sheet on the respective BOUNDSHEET record }
CurrentPos := AStream.Position; CurrentPos := AStream.Position;
@ -482,13 +439,11 @@ begin
AStream.Position := CurrentPos; AStream.Position := CurrentPos;
WriteBOF(AStream, INT_BOF_SHEET); WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream);
WriteIndex(AStream); WriteColInfos(AStream, sheet);
WriteDimensions(AStream, Workbook.GetWorksheetByIndex(i)); WriteDimensions(AStream, sheet);
WriteWindow2(AStream, True); WriteWindow2(AStream, True);
WriteCellsToStream(AStream, sheet.Cells);
WriteCellsToStream(AStream, Workbook.GetWorksheetByIndex(i).Cells);
WriteEOF(AStream); WriteEOF(AStream);
end; end;
@ -515,7 +470,7 @@ begin
AStream.WriteWord(WordToLE(ACol)); AStream.WriteWord(WordToLE(ACol));
{ Index to XF record } { Index to XF record }
AStream.WriteWord(WordToLE(15)); WriteXFIndex(AStream, ACell);
end; end;
{******************************************************************* {*******************************************************************
@ -647,33 +602,53 @@ end;
* The font data is passed in an instance of TFPCustomFont * 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 var
Len: Byte; Len: Byte;
optn: Word;
begin 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 } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
AStream.WriteWord(WordToLE(14 + 1 + Len)); AStream.WriteWord(WordToLE(14 + 1 + Len));
{ Height of the font in twips = 1/20 of a point } { Height of the font in twips = 1/20 of a point }
AStream.WriteWord(WordToLE(200)); AStream.WriteWord(WordToLE(round(AFont.Size*20)));
{ Option flags } { 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 } { Colour index }
AStream.WriteWord($7FFF); AStream.WriteWord(WordToLE(ord(AFont.Color)));
{ Font weight } { 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 } { Escapement type }
AStream.WriteWord(0); AStream.WriteWord(0);
{ Underline type } { Underline type }
AStream.WriteByte(0); if fssUnderline in AFont.Style then
AStream.WriteByte(1)
else
AStream.WriteByte(0);
{ Font family } { Font family }
AStream.WriteByte(0); AStream.WriteByte(0);
@ -686,11 +661,26 @@ begin
{ Font name: Byte string, 8-bit length } { Font name: Byte string, 8-bit length }
AStream.WriteByte(Len); AStream.WriteByte(Len);
AStream.WriteBuffer(AFont.Name[1], Len); AStream.WriteBuffer(AFont.FontName[1], Len);
end; 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 * DESCRIPTION: Writes an Excel 5 FORMULA record
* *
@ -725,7 +715,7 @@ begin
AStream.WriteWord(WordToLE(ACol)); AStream.WriteWord(WordToLE(ACol));
{ Index to XF Record } { Index to XF Record }
AStream.WriteWord($0000); WriteXFIndex(AStream, ACell);
{ Result of the formula in IEEE 754 floating-point value } { Result of the formula in IEEE 754 floating-point value }
AStream.WriteBuffer(FormulaResult, 8); AStream.WriteBuffer(FormulaResult, 8);
@ -939,7 +929,7 @@ begin
AStream.WriteWord(WordToLE(ACol)); AStream.WriteWord(WordToLE(ACol));
{ Index to XF record } { Index to XF record }
AStream.WriteWord(WordToLE(15)); WriteXFIndex(AStream, ACell);
{ Byte String with 16-bit size } { Byte String with 16-bit size }
AStream.WriteWord(WordToLE(L)); AStream.WriteWord(WordToLE(L));
@ -974,7 +964,7 @@ begin
AStream.WriteWord(WordToLE(ACol)); AStream.WriteWord(WordToLE(ACol));
{ Index to XF record } { Index to XF record }
AStream.WriteWord($0); WriteXFIndex(AStream, ACell);
{ IEE 754 floating-point value } { IEE 754 floating-point value }
AStream.WriteBuffer(AValue, 8); AStream.WriteBuffer(AValue, 8);
@ -1120,14 +1110,23 @@ end;
* *
* DESCRIPTION: Writes an Excel 5 XF record * DESCRIPTION: Writes an Excel 5 XF record
* *
* Writes a number (64-bit floating point) to the sheet
*
*******************************************************************} *******************************************************************}
procedure TsSpreadBIFF5Writer.WriteXF(AStream: TStream; AFontIndex: Word; 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 var
optns: Word;
b: Byte;
dw1, dw2: DWord;
XFOptions: Word; XFOptions: Word;
XFAlignment, XFOrientationAttrib: Byte; XFAlignment: byte;
XFBorderDWord1, XFBorderDWord2: DWord;
begin begin
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_XF)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_XF));
@ -1137,43 +1136,263 @@ begin
AStream.WriteWord(WordToLE(AFontIndex)); AStream.WriteWord(WordToLE(AFontIndex));
{ Index to FORMAT record } { Index to FORMAT record }
AStream.WriteWord($00); AStream.WriteWord(WordToLE(AFormatIndex));
{ XF type, cell protection and parent style XF } { 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 if AXF_TYPE_PROT and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then
XFOptions := XFOptions or MASK_XF_TYPE_PROT_PARENT; optns := optns or MASK_XF_TYPE_PROT_PARENT;
AStream.WriteWord(WordToLE(optns));
AStream.WriteWord(WordToLE(XFOptions));
{ Alignment and text break } { 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 rotation }
AStream.WriteByte(ATextRotation); // 0 is horizontal / normal
{ 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));
{ Cell border lines and background area } { 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; end;
{*******************************************************************
* Initialization section
*
* Registers this reader / writer on fpSpreadsheet
*
*******************************************************************}
{ TsSpreadBIFF5Reader } { TsSpreadBIFF5Reader }
@ -1184,6 +1403,9 @@ var
RecordType: Word; RecordType: Word;
CurStreamPos: Int64; CurStreamPos: Int64;
begin begin
// Clear existing fonts. They will be replaced by those from the file.
FWorkbook.RemoveAllFonts;
while (not SectionEOF) do while (not SectionEOF) do
begin begin
{ Read the record header } { Read the record header }
@ -1193,9 +1415,12 @@ begin
CurStreamPos := AStream.Position; CurStreamPos := AStream.Position;
case RecordType of case RecordType of
INT_EXCEL_ID_BOF: ; INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET: ReadBoundSheet(AStream); INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_EOF: SectionEOF := True; 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 else
// nothing // nothing
end; 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_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_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_MULRK: ReadMulRKValues(AStream);
INT_EXCEL_ID_COLINFO: ReadColInfo(AStream);
INT_EXCEL_ID_ROWINFO: ReadRowInfo(AStream); INT_EXCEL_ID_ROWINFO: ReadRowInfo(AStream);
INT_EXCEL_ID_FORMULA: ReadFormulaExcel(AStream); INT_EXCEL_ID_FORMULA: ReadFormulaExcel(AStream);
INT_EXCEL_ID_BOF: ; INT_EXCEL_ID_BOF: ;
@ -1437,17 +1663,6 @@ begin
Result:=Number; Result:=Number;
end; 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); procedure TsSpreadBIFF5Reader.ReadFromFile(AFileName: string; AData: TsWorkbook);
var var
MemStream: TMemoryStream; MemStream: TMemoryStream;
@ -1476,6 +1691,71 @@ begin
end; end;
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); procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var var
BIFF5EOF: Boolean; BIFF5EOF: Boolean;
@ -1525,6 +1805,68 @@ begin
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
end; 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); procedure TsSpreadBIFF5Reader.ReadFormula(AStream: TStream);
begin begin
@ -1569,12 +1911,6 @@ begin
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
end; end;
procedure TsSpreadBIFF5Reader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Integer);
begin
// to do...
end;
initialization initialization

View File

@ -63,29 +63,6 @@ uses
fpsutils, lazutf8; fpsutils, lazutf8;
type 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 TFormatRecordData = class
public public
Index: Integer; Index: Integer;
@ -101,10 +78,9 @@ type
FWorksheetNames: TStringList; FWorksheetNames: TStringList;
FCurrentWorksheet: Integer; FCurrentWorksheet: Integer;
FSharedStringTable: TStringList; FSharedStringTable: TStringList;
FXFList: TFPList; // of TXFRecordData
FFormatList: TFPList; // of TFormatRecordData FFormatList: TFPList; // of TFormatRecordData
function DecodeRKValue(const ARK: DWORD): Double; function DecodeRKValue(const ARK: DWORD): Double;
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer); // procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer);
procedure ExtractNumberFormat(AXFIndex: WORD; procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Word; out ANumberFormat: TsNumberFormat; out ADecimals: Word;
out ANumberFormatStr: String); out ANumberFormatStr: String);
@ -119,9 +95,8 @@ type
procedure ReadRKValue(const AStream: TStream); procedure ReadRKValue(const AStream: TStream);
procedure ReadMulRKValues(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; function ReadString(const AStream: TStream; const ALength: WORD): UTF8String;
procedure ReadRichString(const AStream: TStream);
procedure ReadSST(const AStream: TStream); procedure ReadSST(const AStream: TStream);
procedure ReadLabelSST(const AStream: TStream); procedure ReadLabelSST(const AStream: TStream);
// Read XF record // Read XF record
@ -135,13 +110,12 @@ type
// procedure ReadCodepage in xlscommon // procedure ReadCodepage in xlscommon
// procedure ReadDateMode in xlscommon // procedure ReadDateMode in xlscommon
procedure ReadFont(const AStream: TStream); procedure ReadFont(const AStream: TStream);
// Read col info
procedure ReadColInfo(const AStream: TStream);
{ Record reading methods } { Record reading methods }
procedure ReadBlank(AStream: TStream); override; procedure ReadBlank(AStream: TStream); override;
procedure ReadFormula(AStream: TStream); override; procedure ReadFormula(AStream: TStream); override;
procedure ReadLabel(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override;
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
procedure ReadRichString(const AStream: TStream);
public public
constructor Create(AWorkbook: TsWorkbook); override; constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override; destructor Destroy; override;
@ -164,12 +138,8 @@ type
ACell: PCell); override; ACell: PCell); override;
procedure WriteBOF(AStream: TStream; ADataType: Word); procedure WriteBOF(AStream: TStream; ADataType: Word);
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; 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; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override; const AValue: TDateTime; ACell: PCell); override;
// procedure WriteDateMode in xlscommon; Workbook Globals record
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteEOF(AStream: TStream); procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TsFont); procedure WriteFont(AStream: TStream; AFont: TsFont);
@ -279,7 +249,6 @@ const
INT_EXCEL_ID_BLANK = $0201; INT_EXCEL_ID_BLANK = $0201;
INT_EXCEL_ID_BOF = $0809; INT_EXCEL_ID_BOF = $0809;
INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs 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_COUNTRY = $008C;
INT_EXCEL_ID_EOF = $000A; INT_EXCEL_ID_EOF = $000A;
INT_EXCEL_ID_DIMENSIONS = $0200; INT_EXCEL_ID_DIMENSIONS = $0200;
@ -291,7 +260,6 @@ const
INT_EXCEL_ID_STYLE = $0293; INT_EXCEL_ID_STYLE = $0293;
INT_EXCEL_ID_WINDOW1 = $003D; INT_EXCEL_ID_WINDOW1 = $003D;
INT_EXCEL_ID_WINDOW2 = $023E; INT_EXCEL_ID_WINDOW2 = $023E;
INT_EXCEL_ID_XF = $00E0;
INT_EXCEL_ID_RSTRING = $00D6; INT_EXCEL_ID_RSTRING = $00D6;
INT_EXCEL_ID_RK = $027E; INT_EXCEL_ID_RK = $027E;
INT_EXCEL_ID_MULRK = $00BD; INT_EXCEL_ID_MULRK = $00BD;
@ -318,10 +286,6 @@ const
INT_BOF_BUILD_ID = $1FD2; INT_BOF_BUILD_ID = $1FD2;
INT_BOF_BUILD_YEAR = $07CD; INT_BOF_BUILD_YEAR = $07CD;
{ FONT record constants }
INT_FONT_WEIGHT_NORMAL = $0190;
INT_FONT_WEIGHT_BOLD = $02BC;
{ FORMULA record constants } { FORMULA record constants }
MASK_FORMULA_RECALCULATE_ALWAYS = $0001; MASK_FORMULA_RECALCULATE_ALWAYS = $0001;
MASK_FORMULA_RECALCULATE_ON_OPEN = $0002; MASK_FORMULA_RECALCULATE_ON_OPEN = $0002;
@ -352,44 +316,11 @@ const
{ XF substructures } { 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 }
XF_ROTATION_HORIZONTAL = 0; XF_ROTATION_HORIZONTAL = 0;
XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE = 90; XF_ROTATION_90DEG_CCW = 90;
XF_ROTATION_90_DEGREE_CLOCKWISE = 180; XF_ROTATION_90DEG_CW = 180;
XF_ROTATION_STACKED = 255; // Letters stacked top to bottom, but not rotated
{ XF CELL BORDER } { XF CELL BORDER }
MASK_XF_BORDER_LEFT = $0000000F; MASK_XF_BORDER_LEFT = $0000000F;
@ -397,18 +328,6 @@ const
MASK_XF_BORDER_TOP = $00000F00; MASK_XF_BORDER_TOP = $00000F00;
MASK_XF_BORDER_BOTTOM = $0000F000; 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 } { TsSpreadBIFF8Writer }
@ -425,6 +344,7 @@ begin
Exit; Exit;
end; end;
{
if ACell^.UsedFormattingFields = [uffTextRotation] then if ACell^.UsedFormattingFields = [uffTextRotation] then
begin begin
case ACell^.TextRotation of case ACell^.TextRotation of
@ -435,6 +355,7 @@ begin
end; end;
Exit; Exit;
end; end;
}
{ {
uffNumberFormat does not seem to have default XF indexes, but perhaps look at XF_21 uffNumberFormat does not seem to have default XF indexes, but perhaps look at XF_21
@ -553,8 +474,9 @@ begin
begin begin
case FFormattingStyles[i].TextRotation of case FFormattingStyles[i].TextRotation of
trHorizontal: lTextRotation := XF_ROTATION_HORIZONTAL; trHorizontal: lTextRotation := XF_ROTATION_HORIZONTAL;
rt90DegreeClockwiseRotation: lTextRotation := XF_ROTATION_90_DEGREE_CLOCKWISE; rt90DegreeClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CW;
rt90DegreeCounterClockwiseRotation: lTextRotation := XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE; rt90DegreeCounterClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CCW;
rtStacked: lTextRotation := XF_ROTATION_STACKED;
end; end;
end; end;
@ -680,17 +602,11 @@ begin
AStream.Position := CurrentPos; AStream.Position := CurrentPos;
WriteBOF(AStream, INT_BOF_SHEET); WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream);
WriteIndex(AStream); WriteColInfos(AStream, sheet);
WriteDimensions(AStream, sheet);
WriteColInfos(AStream, sheet); WriteWindow2(AStream, True);
WriteCellsToStream(AStream, sheet.Cells);
WriteDimensions(AStream, sheet);
WriteWindow2(AStream, True);
WriteCellsToStream(AStream, sheet.Cells);
WriteEOF(AStream); WriteEOF(AStream);
end; end;
@ -800,42 +716,6 @@ begin
AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar)); AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar));
end; 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 () * TsSpreadBIFF8Writer.WriteDateTime ()
* *
@ -949,7 +829,6 @@ begin
AStream.WriteWord(WordToLE(optn)); AStream.WriteWord(WordToLE(optn));
{ Colour index } { Colour index }
//AStream.WriteWord(WordToLE(8 + ord(AFont.Color))); //WordToLE($7FFF));
AStream.WriteWord(WordToLE(ord(AFont.Color))); AStream.WriteWord(WordToLE(ord(AFont.Color)));
{ Font weight } { Font weight }
@ -1643,6 +1522,22 @@ end;
{ TsSpreadBIFF8Reader } { 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; function TsSpreadBIFF8Reader.DecodeRKValue(const ARK: DWORD): Double;
var var
Number: Double; Number: Double;
@ -1701,7 +1596,7 @@ const
0, 0, 0, 0, 0, 0, 0, 0); // 51..58 0, 0, 0, 0, 0, 0, 0, 0); // 51..58
var var
lFormatData: TFormatRecordData; lFormatData: TFormatRecordData;
lXFData: TXFRecordData; lXFData: TXFListData;
isAMPM: Boolean; isAMPM: Boolean;
isLongTime: Boolean; isLongTime: Boolean;
isMilliSec: Boolean; isMilliSec: Boolean;
@ -1719,7 +1614,7 @@ begin
if lFormatData = nil then begin if lFormatData = nil then begin
// no custom format, so first test for default formats // no custom format, so first test for default formats
lXFData := TXFRecordData (FXFList.Items[AXFIndex]); lXFData := TXFListData (FXFList.Items[AXFIndex]);
case lXFData.FormatIndex of case lXFData.FormatIndex of
FORMAT_DATE_DM: FORMAT_DATE_DM:
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end; begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end;
@ -1861,7 +1756,7 @@ begin
Result := DecomprStrValue; Result := DecomprStrValue;
end; end;
if StringFlags and 8 = 8 then begin 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 for j := 1 to RunsCounter do begin
if (PendingRecordSize<=0) then begin if (PendingRecordSize<=0) then begin
//A CONTINUE may happend here //A CONTINUE may happend here
@ -1909,14 +1804,13 @@ var
begin begin
// Clear existing fonts. They will be replaced by those from the file. // Clear existing fonts. They will be replaced by those from the file.
FWorkbook.RemoveAllFonts; FWorkbook.RemoveAllFonts;
if Assigned(FSharedStringTable) then FreeAndNil(FSharedStringTable); if Assigned(FSharedStringTable) then FreeAndNil(FSharedStringTable);
while (not SectionEOF) do
begin while (not SectionEOF) do begin
{ Read the record header } { Read the record header }
RecordType := WordLEToN(AStream.ReadWord); RecordType := WordLEToN(AStream.ReadWord);
RecordSize := WordLEToN(AStream.ReadWord); RecordSize := WordLEToN(AStream.ReadWord);
PendingRecordSize:=RecordSize; PendingRecordSize := RecordSize;
CurStreamPos := AStream.Position; CurStreamPos := AStream.Position;
@ -2086,86 +1980,12 @@ begin
end; 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;
{ 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; function TsSpreadBIFF8Reader.ReadString(const AStream: TStream;
const ALength: WORD): UTF8String; const ALength: WORD): UTF8String;
begin begin
Result:=UTF16ToUTF8(ReadWideString(AStream, ALength)); Result:=UTF16ToUTF8(ReadWideString(AStream, ALength));
end; 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); procedure TsSpreadBIFF8Reader.ReadFromFile(AFileName: string; AData: TsWorkbook);
var var
MemStream: TMemoryStream; MemStream: TMemoryStream;
@ -2466,13 +2286,13 @@ type
Border_Background_3: DWord; // Offset 18, Size 2 Border_Background_3: DWord; // Offset 18, Size 2
end; end;
var var
lData: TXFRecordData; lData: TXFListData;
xf: TXFRecord; xf: TXFRecord;
b: Byte; b: Byte;
begin begin
AStream.ReadBuffer(xf, SizeOf(xf)); AStream.ReadBuffer(xf, SizeOf(xf));
lData := TXFRecordData.Create; lData := TXFListData.Create;
// Font index // Font index
lData.FontIndex := WordLEToN(xf.FontIndex); lData.FontIndex := WordLEToN(xf.FontIndex);
@ -2541,12 +2361,12 @@ end;
function TsSpreadBIFF8Reader.FindFormatRecordForCell(const AXFIndex: Integer function TsSpreadBIFF8Reader.FindFormatRecordForCell(const AXFIndex: Integer
): TFormatRecordData; ): TFormatRecordData;
var var
lXFData: TXFRecordData; lXFData: TXFListData;
lFormatData: TFormatRecordData; lFormatData: TFormatRecordData;
i: Integer; i: Integer;
begin begin
Result := nil; Result := nil;
lXFData := TXFRecordData(FXFList.Items[AXFIndex]); lXFData := TXFListData(FXFList.Items[AXFIndex]);
for i := 0 to FFormatList.Count-1 do for i := 0 to FFormatList.Count-1 do
begin begin
lFormatData := TFormatRecordData(FFormatList.Items[i]); lFormatData := TFormatRecordData(FFormatList.Items[i]);
@ -2614,24 +2434,6 @@ begin
FWorkbook.AddFont(font); FWorkbook.AddFont(font);
end; 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 * Initialization section

View File

@ -18,8 +18,14 @@ const
{ RECORD IDs which didn't change across versions 2-8 } { RECORD IDs which didn't change across versions 2-8 }
INT_EXCEL_ID_FONT = $0031; INT_EXCEL_ID_FONT = $0031;
INT_EXCEL_ID_CODEPAGE = $0042; INT_EXCEL_ID_CODEPAGE = $0042;
INT_EXCEL_ID_COLINFO = $007D;
INT_EXCEL_ID_DATEMODE = $0022; INT_EXCEL_ID_DATEMODE = $0022;
INT_EXCEL_ID_PALETTE = $0092; 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 } { Formula constants TokenID values }
@ -183,46 +189,6 @@ const
// 1AH tSheet Start of external sheet reference (BIFF2-BIFF4) // 1AH tSheet Start of external sheet reference (BIFF2-BIFF4)
// 1BH tEndSheet End 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 } { CODEPAGE record constants }
WORD_ASCII = 367; WORD_ASCII = 367;
WORD_UTF_16 = 1200; // BIFF 8 WORD_UTF_16 = 1200; // BIFF 8
@ -267,6 +233,57 @@ const
FORMAT_TIME_MSZ = 47; //time MM:SS.0 FORMAT_TIME_MSZ = 47; //time MM:SS.0
FORMAT_TIME_INTERVAL = 46; //time [hh]:mm:ss, hh can be >24 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 type
TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28 TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28
@ -280,25 +297,41 @@ type
(const ADateTime: TDateTime; ADateMode: TDateMode): Double; (const ADateTime: TDateTime; ADateMode: TDateMode): Double;
type 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 }
TsSpreadBIFFReader = class(TsCustomSpreadReader) TsSpreadBIFFReader = class(TsCustomSpreadReader)
protected protected
FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding
FDateMode: TDateMode; FDateMode: TDateMode;
FPaletteFound: Boolean; FPaletteFound: Boolean;
// converts an Excel color index to a color value. FXFList: TFPList;
// function ExcelPaletteToFPSColor(AIndex: Word): TsColor; procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Integer); virtual;
// Here we can add reading of records which didn't change across BIFF2-8 versions // Here we can add reading of records which didn't change across BIFF5-8 versions
// Workbook Globals records
procedure ReadCodePage(AStream: TStream); procedure ReadCodePage(AStream: TStream);
// Read column info
procedure ReadColInfo(const AStream: TStream);
// Figures out what the base year for dates is for this file // Figures out what the base year for dates is for this file
procedure ReadDateMode(AStream: TStream); procedure ReadDateMode(AStream: TStream);
// Read palette // Read palette
procedure ReadPalette(AStream: TStream); 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 // Read row info
procedure ReadRowInfo(AStream: TStream); virtual; procedure ReadRowInfo(AStream: TStream); virtual;
public public
constructor Create(AWorkbook: TsWorkbook); override; constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
end; end;
{ TsSpreadBIFFWriter } { TsSpreadBIFFWriter }
@ -308,16 +341,17 @@ type
FDateMode: TDateMode; FDateMode: TDateMode;
FLastRow: Integer; FLastRow: Integer;
FLastCol: Word; FLastCol: Word;
// function FPSColorToExcelPalette(AColor: TsColor): Word;
procedure GetLastRowCallback(ACell: PCell; AStream: TStream); procedure GetLastRowCallback(ACell: PCell; AStream: TStream);
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer; function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
procedure GetLastColCallback(ACell: PCell; AStream: TStream); procedure GetLastColCallback(ACell: PCell; AStream: TStream);
function GetLastColIndex(AWorksheet: TsWorksheet): Word; function GetLastColIndex(AWorksheet: TsWorksheet): Word;
function FormulaElementKindToExcelTokenID(AElementKind: TFEKind; out ASecondaryID: Word): 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 // Write out used codepage for character encoding
procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding); 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 // Writes out DATEMODE record depending on FDateMode
procedure WriteDateMode(AStream: TStream); procedure WriteDateMode(AStream: TStream);
// Writes out a PALETTE record containing all colors defined in the workbook // Writes out a PALETTE record containing all colors defined in the workbook
@ -387,40 +421,70 @@ begin
end; end;
end; end;
{ TsSpreadBIFFReader } { TsSpreadBIFFReader }
constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook); constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FXFList := TFPList.Create;
// Initial base date in case it won't be read from file // Initial base date in case it won't be read from file
FDateMode := dm1900; FDateMode := dm1900;
end; end;
(*
function TsSpreadBIFFReader.ExcelPaletteToFPSColor(AIndex: Word): TsColor; destructor TsSpreadBIFFReader.Destroy;
var
j: integer;
begin begin
case AIndex of for j := FXFList.Count-1 downto 0 do TObject(FXFList[j]).Free;
BUILT_IN_COLOR_PALLETE_BLACK : Result := scBlack; FXFList.Free;
BUILT_IN_COLOR_PALLETE_WHITE: Result := scWhite; inherited Destroy;
BUILT_IN_COLOR_PALLETE_RED: Result := scRed; end;
BUILT_IN_COLOR_PALLETE_GREEN: Result := scGreen;
BUILT_IN_COLOR_PALLETE_BLUE: Result := scBlue; { Applies the XF formatting given by the given index to the specified cell }
BUILT_IN_COLOR_PALLETE_YELLOW: Result := scYellow; procedure TsSpreadBIFFReader.ApplyCellFormatting(ARow, ACol: Cardinal;
BUILT_IN_COLOR_PALLETE_MAGENTA: Result := scMagenta; XFIndex: Integer);
BUILT_IN_COLOR_PALLETE_CYAN: Result := scCyan; var
BUILT_IN_COLOR_PALLETE_DARK_RED: Result := scDarkRed; lCell: PCell;
BUILT_IN_COLOR_PALLETE_DARK_GREEN: Result := scDarkGreen; XFData: TXFListData;
BUILT_IN_COLOR_PALLETE_DARK_BLUE: Result := scDarkBlue; begin
BUILT_IN_COLOR_PALLETE_OLIVE: Result := scOlive; lCell := FWorksheet.GetCell(ARow, ACol);
BUILT_IN_COLOR_PALLETE_PURPLE: Result := scPurple; if Assigned(lCell) then begin
BUILT_IN_COLOR_PALLETE_TEAL: Result := scTeal; XFData := TXFListData(FXFList.Items[XFIndex]);
BUILT_IN_COLOR_PALLETE_SILVER: Result := scSilver;
BUILT_IN_COLOR_PALLETE_GREY: Result := scGrey; // Font
// if XFData.FontIndex = 1 then
EXTRA_COLOR_PALETTE_GREY10PCT: Result := scGrey10pct; Include(lCell^.UsedFormattingFields, uffBold)
EXTRA_COLOR_PALETTE_GREY20PCT: Result := scGrey20pct; 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;
end; end;
*)
// In BIFF 8 it seams to always use the UTF-16 codepage // In BIFF 8 it seams to always use the UTF-16 codepage
procedure TsSpreadBIFFReader.ReadCodePage(AStream: TStream); procedure TsSpreadBIFFReader.ReadCodePage(AStream: TStream);
var var
@ -476,6 +540,24 @@ begin
end; end;
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); procedure TsSpreadBIFFReader.ReadDateMode(AStream: TStream);
var var
lBaseMode: Word; lBaseMode: Word;
@ -514,6 +596,18 @@ begin
FPaletteFound := true; FPaletteFound := true;
end; 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 // Read the part of the ROW record that is common to all BIFF versions
procedure TsSpreadBIFFReader.ReadRowInfo(AStream: TStream); procedure TsSpreadBIFFReader.ReadRowInfo(AStream: TStream);
type type
@ -768,6 +862,37 @@ begin
AStream.WriteWord(WordToLE(lCodepage)); AStream.WriteWord(WordToLE(lCodepage));
end; 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); procedure TsSpreadBIFFWriter.WriteDateMode(AStream: TStream);
begin begin
{ BIFF Record header } { BIFF Record header }