fpspreadsheet: Add support for empty cells to all BIFF formats (needed for formatting of empty cells).

Fix painting error of cell borders in fpspreadsheetgrid.
Add/complete reading/writing support for horizontal alignment, cell background and cell borders to BIFF2.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2958 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2014-04-21 21:43:43 +00:00
parent 4f85834153
commit f7f1b0f12a
12 changed files with 465 additions and 113 deletions

View File

@ -1,21 +1,23 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<CONFIG> <CONFIG>
<ProjectOptions> <ProjectOptions>
<Version Value="9"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<Version Value="7"/>
<General> <General>
<Flags> <Flags>
<LRSInOutputDirectory Value="False"/> <LRSInOutputDirectory Value="False"/>
</Flags> </Flags>
<SessionStorage Value="InProjectDir"/> <SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/> <MainUnit Value="0"/>
<TargetFileExt Value=".exe"/>
<Title Value="excel2read"/> <Title Value="excel2read"/>
<UseAppBundle Value="False"/> <UseAppBundle Value="False"/>
</General> </General>
<VersionInfo> <VersionInfo>
<ProjectVersion Value=""/> <StringTable ProductVersion=""/>
</VersionInfo> </VersionInfo>
<BuildModes Count="1">
<Item1 Name="default" Default="True"/>
</BuildModes>
<PublishOptions> <PublishOptions>
<Version Value="2"/> <Version Value="2"/>
<IgnoreBinaries Value="False"/> <IgnoreBinaries Value="False"/>
@ -42,12 +44,17 @@
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>
<Version Value="8"/> <Version Value="11"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<SearchPaths> <SearchPaths>
<OtherUnitFiles Value="..\"/> <OtherUnitFiles Value=".."/>
<SrcPath Value="..\"/> <SrcPath Value=".."/>
</SearchPaths> </SearchPaths>
<Parsing>
<SyntaxOptions>
<UseAnsiStrings Value="False"/>
</SyntaxOptions>
</Parsing>
<Other> <Other>
<CompilerPath Value="$(CompPath)"/> <CompilerPath Value="$(CompPath)"/>
</Other> </Other>

View File

@ -1,21 +1,23 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<CONFIG> <CONFIG>
<ProjectOptions> <ProjectOptions>
<Version Value="9"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<Version Value="7"/>
<General> <General>
<Flags> <Flags>
<LRSInOutputDirectory Value="False"/> <LRSInOutputDirectory Value="False"/>
</Flags> </Flags>
<SessionStorage Value="InProjectDir"/> <SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/> <MainUnit Value="0"/>
<TargetFileExt Value=".exe"/>
<Title Value="excel2write"/> <Title Value="excel2write"/>
<UseAppBundle Value="False"/> <UseAppBundle Value="False"/>
</General> </General>
<VersionInfo> <VersionInfo>
<ProjectVersion Value=""/> <StringTable ProductVersion=""/>
</VersionInfo> </VersionInfo>
<BuildModes Count="1">
<Item1 Name="default" Default="True"/>
</BuildModes>
<PublishOptions> <PublishOptions>
<Version Value="2"/> <Version Value="2"/>
<IgnoreBinaries Value="False"/> <IgnoreBinaries Value="False"/>
@ -42,12 +44,17 @@
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>
<Version Value="8"/> <Version Value="11"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<SearchPaths> <SearchPaths>
<OtherUnitFiles Value="..\"/> <OtherUnitFiles Value=".."/>
<SrcPath Value="..\"/> <SrcPath Value=".."/>
</SearchPaths> </SearchPaths>
<Parsing>
<SyntaxOptions>
<UseAnsiStrings Value="False"/>
</SyntaxOptions>
</Parsing>
<Other> <Other>
<CompilerPath Value="$(CompPath)"/> <CompilerPath Value="$(CompPath)"/>
</Other> </Other>

View File

@ -58,6 +58,27 @@ begin
// Write current date/time // Write current date/time
MyWorksheet.WriteDateTime(2, 0, now); MyWorksheet.WriteDateTime(2, 0, now);
// Write cell with background color
MyWorksheet.WriteUTF8Text(3, 0, 'Text');
MyWorksheet.WriteBackgroundColor(3, 0, scSilver);
// Empty cell with background color
MyWorksheet.WriteBackgroundColor(3, 1, scGrey);
// Cell2 with top and bottom borders
MyWorksheet.WriteUTF8Text(4, 0, 'Text');
MyWorksheet.WriteBorders(4, 0, [cbNorth, cbSouth]);
MyWorksheet.WriteBorders(4, 1, [cbNorth, cbSouth]);
MyWorksheet.WriteBorders(4, 2, [cbNorth, cbSouth]);
// Left, center, right aligned texts
MyWorksheet.WriteUTF8Text(5, 0, 'L');
MyWorksheet.WriteUTF8Text(5, 1, 'C');
MyWorksheet.WriteUTF8Text(5, 2, 'R');
MyWorksheet.WriteHorAlignment(5, 0, haLeft);
MyWorksheet.WriteHorAlignment(5, 1, haCenter);
MyWorksheet.WriteHorAlignment(5, 2, haRight);
// Save the spreadsheet to a file // Save the spreadsheet to a file
MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true); MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true);
MyWorkbook.Free; MyWorkbook.Free;

View File

@ -52,6 +52,12 @@ begin
lCell^.BackgroundColor := scPURPLE; lCell^.BackgroundColor := scPURPLE;
lCell^.UsedFormattingFields := [uffBackgroundColor]; lCell^.UsedFormattingFields := [uffBackgroundColor];
// E6 empty cell, only background color
MyWorksheet.WriteBackgroundColor(5, 4, scYellow);
// E7 empty cell, only all borders
MyWorksheet.WriteBorders(5, 5, [cbNorth, cbEast, cbSouth, cbWest]);
// Word-wrapped long text in D7 // Word-wrapped long text in D7
MyWorksheet.WriteUTF8Text(6, 3, 'This is a very, very, very, very long text.'); MyWorksheet.WriteUTF8Text(6, 3, 'This is a very, very, very, very long text.');
MyWorksheet.WriteUsedFormatting(6, 3, [uffWordwrap]); MyWorksheet.WriteUsedFormatting(6, 3, [uffWordwrap]);
@ -65,6 +71,7 @@ begin
MyWorksheet.WriteAnsiText(i, 3, ParamStr(0)); MyWorksheet.WriteAnsiText(i, 3, ParamStr(0));
end; end;
} }
// Write the formula E1 = A1 + B1 // Write the formula E1 = A1 + B1
SetLength(MyRPNFormula, 3); SetLength(MyRPNFormula, 3);
MyRPNFormula[0].ElementKind := fekCell; MyRPNFormula[0].ElementKind := fekCell;
@ -84,8 +91,6 @@ begin
MyRPNFormula[1].ElementKind := fekABS; MyRPNFormula[1].ElementKind := fekABS;
MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula); MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula);
//MyFormula.FormulaStr := '';
// Write current date/time to cells B11:B16 // Write current date/time to cells B11:B16
MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate'); MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate');
MyWorksheet.WriteDateTime(10, 1, now, nfShortDate); MyWorksheet.WriteDateTime(10, 1, now, nfShortDate);

View File

@ -58,7 +58,7 @@ var
implementation implementation
uses uses
Grids; Grids, fpcanvas;
{ TForm1 } { TForm1 }
@ -95,7 +95,10 @@ begin
if OpenDialog1.Execute then if OpenDialog1.Execute then
begin begin
sWorksheetGrid1.LoadFromSpreadsheetFile(OpenDialog1.FileName); sWorksheetGrid1.LoadFromSpreadsheetFile(OpenDialog1.FileName);
Caption := Format('fpsGrid - %s', [OpenDialog1.Filename]); Caption := Format('fpsGrid - %s (%s)', [
OpenDialog1.Filename,
GetFileFormatName(sWorksheetGrid1.Workbook.FileFormat)
]);
// Create a tab in the pagecontrol for each worksheet contained in the workbook // Create a tab in the pagecontrol for each worksheet contained in the workbook
// This would be easer with a TTabControl. This has display issues, though. // This would be easer with a TTabControl. This has display issues, though.

View File

@ -90,10 +90,16 @@ type
// Routines to write parts of those files // Routines to write parts of those files
function WriteStylesXMLAsString: string; function WriteStylesXMLAsString: string;
{ Record writing methods } { Record writing methods }
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); override; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; ACell: PCell); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override; const AFormula: TsFormula; ACell: PCell); override;
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 WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override;
public public
constructor Create; override; constructor Create; override;
{ General writing methods } { General writing methods }
@ -805,6 +811,17 @@ begin
</table:table-cell>} </table:table-cell>}
end; end;
{
Writes an empty cell
Not clear whether this is needed for ods, but the inherited procedure is abstract.
}
procedure TsSpreadOpenDocWriter.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell);
begin
// no action at the moment...
end;
{ {
Writes a cell with text content Writes a cell with text content

View File

@ -296,6 +296,7 @@ type
procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring);
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2); AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2);
procedure WriteBlank(ARow, ACol: Cardinal);
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
@ -304,6 +305,7 @@ type
procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation); procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation);
procedure WriteUsedFormatting(ARow, ACol: Cardinal; AUsedFormatting: TsUsedFormattingFields); procedure WriteUsedFormatting(ARow, ACol: Cardinal; AUsedFormatting: TsUsedFormattingFields);
procedure WriteBackgroundColor(ARow, ACol: Cardinal; AColor: TsColor); procedure WriteBackgroundColor(ARow, ACol: Cardinal; AColor: TsColor);
procedure WriteBorders(ARow, ACol: Cardinal; ABorders: TsCellBorders);
procedure WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment); procedure WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment);
procedure WriteVertAlignment(ARow, ACol: Cardinal; AValue: TsVertAlignment); procedure WriteVertAlignment(ARow, ACol: Cardinal; AValue: TsVertAlignment);
{ Data manipulation methods - For Rows and Cols } { Data manipulation methods - For Rows and Cols }
@ -328,6 +330,7 @@ type
{ Internal data } { Internal data }
FWorksheets: TFPList; FWorksheets: TFPList;
FEncoding: TsEncoding; FEncoding: TsEncoding;
FFormat: TsSpreadsheetFormat;
{ Internal methods } { Internal methods }
procedure RemoveCallback(data, arg: pointer); procedure RemoveCallback(data, arg: pointer);
public public
@ -356,6 +359,7 @@ type
{@@ This property is only used for formats which don't support unicode {@@ This property is only used for formats which don't support unicode
and support a single encoding for the whole document, like Excel 2 to 5 } and support a single encoding for the whole document, like Excel 2 to 5 }
property Encoding: TsEncoding read FEncoding write FEncoding; property Encoding: TsEncoding read FEncoding write FEncoding;
property FileFormat: TsSpreadsheetFormat read FFormat;
end; end;
{@@ TsSpreadReader class reference type } {@@ TsSpreadReader class reference type }
@ -369,6 +373,7 @@ type
FWorkbook: TsWorkbook; FWorkbook: TsWorkbook;
FWorksheet: TsWorksheet; FWorksheet: TsWorksheet;
{ Record reading methods } { Record reading methods }
procedure ReadBlank(AStream: TStream); virtual; abstract;
procedure ReadFormula(AStream: TStream); virtual; abstract; procedure ReadFormula(AStream: TStream); virtual; abstract;
procedure ReadLabel(AStream: TStream); virtual; abstract; procedure ReadLabel(AStream: TStream); virtual; abstract;
procedure ReadNumber(AStream: TStream); virtual; abstract; procedure ReadNumber(AStream: TStream); virtual; abstract;
@ -401,6 +406,7 @@ type
procedure WriteCellCallback(ACell: PCell; AStream: TStream); procedure WriteCellCallback(ACell: PCell; AStream: TStream);
procedure WriteCellsToStream(AStream: TStream; ACells: TAVLTree); procedure WriteCellsToStream(AStream: TStream; ACells: TAVLTree);
{ Record writing methods } { Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual; abstract;
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); virtual; abstract; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); virtual; abstract;
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); virtual; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); virtual;
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); virtual; procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); virtual;
@ -478,6 +484,7 @@ type
function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem; overload; function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem; overload;
function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem; overload; function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem; overload;
var var
GsSpreadFormats: array of TsSpreadFormatData; GsSpreadFormats: array of TsSpreadFormatData;
@ -486,6 +493,8 @@ procedure RegisterSpreadFormat(
AWriterClass: TsSpreadWriterClass; AWriterClass: TsSpreadWriterClass;
AFormat: TsSpreadsheetFormat); AFormat: TsSpreadsheetFormat);
function GetFileFormatName(AFormat: TsSpreadsheetFormat): String;
function SciFloat(AValue: Double; ADecimals: Word): String; function SciFloat(AValue: Double; ADecimals: Word): String;
function TimeIntervalToString(AValue: TDateTime): String; function TimeIntervalToString(AValue: TDateTime): String;
@ -500,6 +509,8 @@ resourcestring
lpUnsupportedReadFormat = 'Tried to read a spreadsheet using an unsupported format'; lpUnsupportedReadFormat = 'Tried to read a spreadsheet using an unsupported format';
lpUnsupportedWriteFormat = 'Tried to write a spreadsheet using an unsupported format'; lpUnsupportedWriteFormat = 'Tried to write a spreadsheet using an unsupported format';
lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file.'; lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file.';
lpUnknownSpreadsheetFormat = 'unknown format';
{@@ {@@
Registers a new reader/writer pair for a format Registers a new reader/writer pair for a format
@ -519,6 +530,27 @@ begin
GsSpreadFormats[len].Format := AFormat; GsSpreadFormats[len].Format := AFormat;
end; end;
{@@
Returns the name of the given file format.
}
function GetFileFormatName(AFormat: TsSpreadsheetFormat): string;
begin
case AFormat of
sfExcel2 : Result := 'BIFF2';
sfExcel3 : Result := 'BIFF3';
sfExcel4 : Result := 'BIFF4';
sfExcel5 : Result := 'BIFF5';
sfExcel8 : Result := 'BIFF8';
sfooxml : Result := 'OOXML';
sfOpenDocument : Result := 'Open Document';
sfCSV : Result := 'CSV';
sfWikiTable_Pipes : Result := 'WikiTable Pipes';
sfWikiTable_WikiMedia : Result := 'WikiTable WikiMedia';
else Result := lpUnknownSpreadsheetFormat;
end;
end;
{@@ {@@
Formats the number AValue in "scientific" format with the given number of Formats the number AValue in "scientific" format with the given number of
decimals. "Scientific" is the same as "exponential", but with exponents rounded decimals. "Scientific" is the same as "exponential", but with exponents rounded
@ -1056,6 +1088,22 @@ begin
end; end;
end; end;
{@@
Writes as empty cell
@param ARow The row of the cell
@param ACol The column of the cell
Note: an empty cell is required for formatting.
}
procedure TsWorksheet.WriteBlank(ARow, ACol: Cardinal);
var
ACell: PCell;
begin
ACell := GetCell(ARow, ACol);
ACell^.ContentType := cctEmpty;
end;
{@@ {@@
Writes a date/time value to a determined cell Writes a date/time value to a determined cell
@ -1128,7 +1176,6 @@ var
ACell: PCell; ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
ACell^.ContentType := cctFormula; ACell^.ContentType := cctFormula;
ACell^.FormulaValue := AFormula; ACell^.FormulaValue := AFormula;
end; end;
@ -1148,7 +1195,6 @@ var
ACell: PCell; ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
Include(ACell^.UsedFormattingFields, uffNumberFormat); Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := ANumberFormat; ACell^.NumberFormat := ANumberFormat;
end; end;
@ -1159,7 +1205,6 @@ var
ACell: PCell; ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
ACell^.ContentType := cctRPNFormula; ACell^.ContentType := cctRPNFormula;
ACell^.RPNFormulaValue := AFormula; ACell^.RPNFormulaValue := AFormula;
end; end;
@ -1179,7 +1224,6 @@ var
ACell: PCell; ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
Include(ACell^.UsedFormattingFields, uffTextRotation); Include(ACell^.UsedFormattingFields, uffTextRotation);
ACell^.TextRotation := ARotation; ACell^.TextRotation := ARotation;
end; end;
@ -1190,7 +1234,6 @@ var
ACell: PCell; ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
ACell^.UsedFormattingFields := AUsedFormatting; ACell^.UsedFormattingFields := AUsedFormatting;
end; end;
@ -1200,11 +1243,19 @@ var
ACell: PCell; ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
ACell^.UsedFormattingFields := ACell^.UsedFormattingFields + [uffBackgroundColor]; ACell^.UsedFormattingFields := ACell^.UsedFormattingFields + [uffBackgroundColor];
ACell^.BackgroundColor := AColor; ACell^.BackgroundColor := AColor;
end; end;
procedure TsWorksheet.WriteBorders(ARow, ACol: Cardinal; ABorders: TsCellBorders);
var
lCell: PCell;
begin
lCell := GetCell(ARow, ACol);
Include(lCell^.UsedFormattingFields, uffBorder);
lCell^.Border := ABorders;
end;
procedure TsWorksheet.WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment); procedure TsWorksheet.WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment);
var var
lCell: PCell; lCell: PCell;
@ -1427,6 +1478,7 @@ begin
try try
AReader.ReadFromFile(AFileName, Self); AReader.ReadFromFile(AFileName, Self);
FFormat := AFormat;
finally finally
AReader.Free; AReader.Free;
end; end;
@ -1893,6 +1945,7 @@ end;
procedure TsCustomSpreadWriter.WriteCellCallback(ACell: PCell; AStream: TStream); procedure TsCustomSpreadWriter.WriteCellCallback(ACell: PCell; AStream: TStream);
begin begin
case ACell.ContentType of case ACell.ContentType of
cctEmpty: WriteBlank(AStream, ACell^.Row, ACell^.Col, ACell);
cctDateTime: WriteDateTime(AStream, ACell^.Row, ACell^.Col, ACell^.DateTimeValue, ACell); cctDateTime: WriteDateTime(AStream, ACell^.Row, ACell^.Col, ACell^.DateTimeValue, ACell);
cctNumber: WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell); cctNumber: WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell);
cctUTF8String: WriteLabel(AStream, ACell^.Row, ACell^.Col, ACell^.UTF8StringValue, ACell); cctUTF8String: WriteLabel(AStream, ACell^.Row, ACell^.Col, ACell^.UTF8StringValue, ACell);

View File

@ -31,7 +31,7 @@ type
protected protected
{ Protected declarations } { Protected declarations }
procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override; procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override;
procedure DrawCellGrid(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState); override; procedure DrawAllRows; override;
procedure DrawTextInCell(ACol, ARow: Integer; ARect: TRect; AState: TGridDrawState); override; procedure DrawTextInCell(ACol, ARow: Integer; ARect: TRect; AState: TGridDrawState); override;
function GetCellText(ACol, ARow: Integer): String; function GetCellText(ACol, ARow: Integer): String;
procedure Loaded; override; procedure Loaded; override;
@ -156,7 +156,23 @@ procedure Register;
implementation implementation
uses uses
fpsUtils; fpCanvas, fpsUtils;
var
FillPattern_BIFF2: TBitmap = nil;
procedure Create_FillPattern_BIFF2(ABkColor: TColor);
begin
FreeAndNil(FillPattern_BIFF2);
FillPattern_BIFF2 := TBitmap.Create;
with FillPattern_BIFF2 do begin
SetSize(4, 4);
Canvas.Brush.Color := ABkColor;
Canvas.FillRect(0, 0, Width, Height);
Canvas.Pixels[0, 0] := clBlack;
Canvas.Pixels[2, 2] := clBlack;
end;
end;
function FPSColorToColor(FPSColor: TsColor): TColor; function FPSColorToColor(FPSColor: TsColor): TColor;
begin begin
@ -180,8 +196,8 @@ begin
// //
scGrey10pct: Result := TColor($00E6E6E6); scGrey10pct: Result := TColor($00E6E6E6);
scGrey20pct: Result := TColor($00CCCCCC); scGrey20pct: Result := TColor($00CCCCCC);
scOrange : Result := TColor($0000A4FF); // FFA500 scOrange : Result := TColor($0000A5FF); // FFA500
scDarkBrown: Result := TColor($002D53A0); // A0522D scDarkBrown: Result := TColor($002D52A0); // A0522D
scBrown : Result := TColor($003F85CD); // CD853F scBrown : Result := TColor($003F85CD); // CD853F
scBeige : Result := TColor($00DCF5F5); // F5F5DC scBeige : Result := TColor($00DCF5F5); // F5F5DC
scWheat : Result := TColor($00B3DEF5); // F5DEB3 scWheat : Result := TColor($00B3DEF5); // F5DEB3
@ -226,6 +242,8 @@ begin
Result := round(AHeight / 25.4 * Screen.PixelsPerInch) + 4; Result := round(AHeight / 25.4 * Screen.PixelsPerInch) + 4;
end; end;
{ Adjusts the grid's canvas before painting a given cell. Considers, e.g.
background color, horizontal alignment, vertical alignment, etc. }
procedure TsCustomWorksheetGrid.DoPrepareCanvas(ACol, ARow: Integer; procedure TsCustomWorksheetGrid.DoPrepareCanvas(ACol, ARow: Integer;
AState: TGridDrawState); AState: TGridDrawState);
var var
@ -233,6 +251,7 @@ var
lCell: PCell; lCell: PCell;
r, c: Integer; r, c: Integer;
begin begin
Canvas.Brush.Bitmap := nil;
ts := Canvas.TextStyle; ts := Canvas.TextStyle;
if FDisplayFixedColRow then begin if FDisplayFixedColRow then begin
// Formatting of row and column headers // Formatting of row and column headers
@ -274,8 +293,15 @@ begin
end; end;
// Background color // Background color
if (uffBackgroundColor in lCell^.UsedFormattingFields) then begin if (uffBackgroundColor in lCell^.UsedFormattingFields) then begin
if FWorkbook.FileFormat = sfExcel2 then begin
if (FillPattern_BIFF2 = nil) and (ComponentState = []) then
Create_FillPattern_BIFF2(Color);
Canvas.Brush.Style := bsImage;
Canvas.Brush.Bitmap := FillPattern_BIFF2;
end else begin
Canvas.Brush.Style := bsSolid; Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := FPSColorToColor(lCell^.BackgroundColor); Canvas.Brush.Color := FPSColorToColor(lCell^.BackgroundColor);
end;
end else begin end else begin
Canvas.Brush.Style := bsSolid; Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := Color; Canvas.Brush.Color := Color;
@ -287,42 +313,46 @@ begin
inherited DoPrepareCanvas(ACol, ARow, AState); inherited DoPrepareCanvas(ACol, ARow, AState);
end; end;
procedure TsCustomWorksheetGrid.DrawCellGrid(ACol, ARow: Integer; ARect: TRect; { Paints the cell borders. This cannot be done in DrawCellGrid because the
AState: TGridDrawState); lower border line is overwritten when painting the next row. }
procedure TsCustomWorksheetGrid.DrawAllRows;
var var
lCell: PCell; cell: PCell;
r, c: Integer; c, r: Integer;
rect: TRect;
begin begin
inherited; inherited;
if FWorksheet = nil then exit; if FWorksheet = nil then exit;
r := ARow - FixedRows; cell := FWorksheet.GetFirstCell;
c := ACol - FixedCols; while cell <> nil do begin
lCell := FWorksheet.FindCell(r, c); if (uffBorder in cell^.UsedFormattingFields) then begin
if (lCell <> nil) and (uffBorder in lCell^.UsedFormattingFields) then begin c := cell^.Col + FixedCols;
r := cell^.Row + FixedRows;
rect := CellRect(c, r);
Canvas.Pen.Style := psSolid; Canvas.Pen.Style := psSolid;
Canvas.Pen.Color := clBlack; Canvas.Pen.Color := clBlack;
if (cbNorth in lCell^.Border) then if (cbNorth in cell^.Border) then
Canvas.Line(ARect.Left, ARect.Top, ARect.Right, ARect.Top) Canvas.Line(rect.Left, rect.Top-1, rect.Right, rect.Top-1);
else if (cbWest in cell^.Border) then
if (cbWest in lCell^.Border) then Canvas.Line(rect.Left-1, rect.Top, rect.Left-1, rect.Bottom);
Canvas.Line(ARect.Left, ARect.Top, ARect.Left, ARect.Bottom) if (cbEast in cell^.Border) then
else Canvas.Line(rect.Right-1, rect.Top, rect.Right-1, rect.Bottom);
if (cbEast in lCell^.Border) then if (cbSouth in cell^.Border) then
Canvas.Line(ARect.Right-1, ARect.Top, ARect.Right-1, ARect.Bottom) Canvas.Line(rect.Left, rect.Bottom-1, rect.Right, rect.Bottom-1);
else end;
if (cbSouth in lCell^.Border) then cell := FWorksheet.GetNextCell;
Canvas.Line(ARect.Left, ARect.Bottom-1, ARect.Right, ARect.Bottom-1)
end; end;
end; end;
{ Draws the cell text. Calls "GetCellText" to determine the text in the cell. }
procedure TsCustomWorksheetGrid.DrawTextInCell(ACol, ARow: Integer; ARect: TRect; procedure TsCustomWorksheetGrid.DrawTextInCell(ACol, ARow: Integer; ARect: TRect;
AState: TGridDrawState); AState: TGridDrawState);
begin begin
DrawCellText(aCol, aRow, aRect, aState, GetCellText(ACol,ARow)); DrawCellText(aCol, aRow, aRect, aState, GetCellText(ACol,ARow));
end; end;
{ This function returns the text to be written in the cell }
function TsCustomWorksheetGrid.GetCellText(ACol, ARow: Integer): String; function TsCustomWorksheetGrid.GetCellText(ACol, ARow: Integer): String;
var var
lCell: PCell; lCell: PCell;
@ -354,6 +384,8 @@ begin
end; end;
end; end;
{ Returns a list of worksheets contained in the file. Useful for assigning to
user controls like TabControl, Combobox etc. in order to select a sheet. }
procedure TsCustomWorksheetGrid.GetSheets(const ASheets: TStrings); procedure TsCustomWorksheetGrid.GetSheets(const ASheets: TStrings);
var var
i: Integer; i: Integer;
@ -488,4 +520,9 @@ begin
LoadFromWorksheet(FWorkbook.GetWorksheetByIndex(AIndex)); LoadFromWorksheet(FWorkbook.GetWorksheetByIndex(AIndex));
end; end;
initialization
finalization
FreeAndNil(FillPattern_BIFF2);
end. end.

View File

@ -47,7 +47,11 @@ type
FWorksheet: TsWorksheet; FWorksheet: TsWorksheet;
procedure ReadRowInfo(AStream: TStream); procedure ReadRowInfo(AStream: TStream);
protected protected
procedure ApplyCellFormatting(ARow, ACol: Word; XF, AFormat, AFont, AStyle: Byte);
procedure ReadRowColStyle(AStream: TStream; out ARow, ACol: Word;
out XF, AFormat, AFont, AStyle: byte);
{ Record writing methods } { Record writing methods }
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;
@ -63,6 +67,7 @@ type
private private
procedure WriteCellFormatting(AStream: TStream; ACell: PCell); procedure WriteCellFormatting(AStream: TStream; ACell: PCell);
{ Record writing methods } { Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override;
procedure WriteBOF(AStream: TStream); procedure WriteBOF(AStream: TStream);
procedure WriteEOF(AStream: TStream); procedure WriteEOF(AStream: TStream);
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override; procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override;
@ -78,6 +83,7 @@ implementation
const const
{ Excel record IDs } { Excel record IDs }
INT_EXCEL_ID_BLANK = $0001;
INT_EXCEL_ID_INTEGER = $0002; INT_EXCEL_ID_INTEGER = $0002;
INT_EXCEL_ID_NUMBER = $0003; INT_EXCEL_ID_NUMBER = $0003;
INT_EXCEL_ID_LABEL = $0004; INT_EXCEL_ID_LABEL = $0004;
@ -100,7 +106,7 @@ const
procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell); procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell);
var var
BorderByte: Byte = 0; b: Byte;
begin begin
if ACell^.UsedFormattingFields = [] then if ACell^.UsedFormattingFields = [] then
begin begin
@ -110,26 +116,36 @@ begin
Exit; Exit;
end; end;
AStream.WriteByte($0); // 1st byte:
// Mask $3F: Index to XF record
// Mask $40: 1 = Cell is locked
// Mask $80: 1 = Formula is hidden
AStream.WriteByte($0); AStream.WriteByte($0);
// The Border and Background // 2nd byte:
// Mask $3F: Index to FORMAT record
// Mask $C0: Index to FONT record
AStream.WriteByte($0);
BorderByte := 0; // 3rd byte
// Mask $07: horizontal alignment
if uffBorder in ACell^.UsedFormattingFields then // Mask $08: Cell has left border
begin // Mask $10: Cell has right border
if cbNorth in ACell^.Border then BorderByte := BorderByte or $20; // Mask $20: Cell has top border
if cbWest in ACell^.Border then BorderByte := BorderByte or $08; // Mask $40: Cell has bottom border
if cbEast in ACell^.Border then BorderByte := BorderByte or $10; // Mask $80: Cell has shaded background
if cbSouth in ACell^.Border then BorderByte := BorderByte or $40; b := 0;
if uffHorAlign in ACell^.UsedFormattingFields then
b := ord (ACell^.HorAlignment);
if uffBorder in ACell^.UsedFormattingFields then begin
if cbNorth in ACell^.Border then b := b or $20;
if cbWest in ACell^.Border then b := b or $08;
if cbEast in ACell^.Border then b := b or $10;
if cbSouth in ACell^.Border then b := b or $40;
end; end;
// BIFF2 does not support a background color, just a "shaded" option
if uffBackgroundColor in ACell^.UsedFormattingFields then if uffBackgroundColor in ACell^.UsedFormattingFields then
BorderByte := BorderByte or $80; b := b or $80;
AStream.WriteByte(b);
AStream.WriteByte(BorderByte);
end; end;
{ {
@ -329,6 +345,29 @@ begin
AStream.position := FinalPos; AStream.position := FinalPos;
end; end;
{*******************************************************************
* TsSpreadBIFF2Writer.WriteBlank ()
*
* DESCRIPTION: Writes an Excel 2 record for an empty cell
*
* Required if this cell should contain formatting
*
*******************************************************************}
procedure TsSpreadBIFF2Writer.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell);
begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK));
AStream.WriteWord(WordToLE(7));
{ BIFF Record data }
AStream.WriteWord(WordToLE(ARow));
AStream.WriteWord(WordToLE(ACol));
{ BIFF2 Attributes }
WriteCellFormatting(AStream, ACell);
end;
{******************************************************************* {*******************************************************************
* TsSpreadBIFF2Writer.WriteLabel () * TsSpreadBIFF2Writer.WriteLabel ()
* *
@ -435,6 +474,48 @@ end;
{ TsSpreadBIFF2Reader } { TsSpreadBIFF2Reader }
procedure TsSpreadBIFF2Reader.ApplyCellFormatting(ARow, ACol: Word;
XF, AFormat, AFont, AStyle: Byte);
var
lCell: PCell;
begin
lCell := FWorksheet.GetCell(ARow, ACol);
if Assigned(lCell) then begin
// Horizontal justification
if AStyle and $07 <> 0 then begin
Include(lCell^.UsedFormattingFields, uffHorAlign);
lCell^.HorAlignment := TsHorAlignment(AStyle and $07);
end;
// Border
if AStyle and $78 <> 0 then begin
Include(lCell^.UsedFormattingFields, uffBorder);
lCell^.Border := [];
if AStyle and $08 <> 0 then Include(lCell^.Border, cbWest);
if AStyle and $10 <> 0 then Include(lCell^.Border, cbEast);
if AStyle and $20 <> 0 then Include(lCell^.Border, cbNorth);
if AStyle and $40 <> 0 then Include(lCell^.Border, cbSouth);
end else
Exclude(lCell^.UsedFormattingFields, uffBorder);
// Background
if AStyle and $80 <> 0 then begin
Include(lCell^.UsedFormattingFields, uffBackgroundColor);
// Background color is ignored
end;
end;
end;
procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream);
var
ARow, ACol: Word;
XF, AFormat, AFont, AStyle: Byte;
begin
ReadRowColStyle(AStream, ARow, ACol, XF, AFormat, AFont, AStyle);
ApplyCellFormatting(ARow, ACol, XF, AFormat, AFont, AStyle);
end;
procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook); procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var var
BIFF2EOF: Boolean; BIFF2EOF: Boolean;
@ -460,6 +541,7 @@ begin
case RecordType of case RecordType of
INT_EXCEL_ID_BLANK: ReadBlank(AStream);
INT_EXCEL_ID_INTEGER: ReadInteger(AStream); INT_EXCEL_ID_INTEGER: ReadInteger(AStream);
INT_EXCEL_ID_NUMBER: ReadNumber(AStream); INT_EXCEL_ID_NUMBER: ReadNumber(AStream);
INT_EXCEL_ID_LABEL: ReadLabel(AStream); INT_EXCEL_ID_LABEL: ReadLabel(AStream);
@ -488,17 +570,12 @@ procedure TsSpreadBIFF2Reader.ReadLabel(AStream: TStream);
var var
L: Byte; L: Byte;
ARow, ACol: Word; ARow, ACol: Word;
XF, AFormat, AFont, AStyle: Byte;
AValue: array[0..255] of Char; AValue: array[0..255] of Char;
AStrValue: UTF8String; AStrValue: UTF8String;
begin begin
{ BIFF Record data } { BIFF Record row/column/style }
ARow := WordLEToN(AStream.ReadWord); ReadRowColStyle(AStream, ARow, ACol, XF, AFormat, AFont, AStyle);
ACol := WordLEToN(AStream.ReadWord);
{ BIFF2 Attributes }
AStream.ReadByte();
AStream.ReadByte();
AStream.ReadByte();
{ String with 8-bit size } { String with 8-bit size }
L := AStream.ReadByte(); L := AStream.ReadByte();
@ -518,48 +595,68 @@ begin
AStrValue := CP1252ToUTF8(AValue); AStrValue := CP1252ToUTF8(AValue);
end; end;
FWorksheet.WriteUTF8Text(ARow, ACol, AStrValue); FWorksheet.WriteUTF8Text(ARow, ACol, AStrValue);
{ Apply formatting to cell }
ApplyCellFormatting(ARow, ACol, XF, AFormat, AFont, AStyle);
end; end;
procedure TsSpreadBIFF2Reader.ReadNumber(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadNumber(AStream: TStream);
var var
ARow, ACol: Word; ARow, ACol: Word;
XF, AFormat, AFont, AStyle: Byte;
AValue: Double; AValue: Double;
begin begin
{ BIFF Record data } { BIFF Record row/column/style }
ARow := WordLEToN(AStream.ReadWord); ReadRowColStyle(AStream, ARow, ACol, XF, AFormat, AFont, AStyle);
ACol := WordLEToN(AStream.ReadWord);
{ BIFF2 Attributes }
AStream.ReadByte();
AStream.ReadByte();
AStream.ReadByte();
{ IEE 754 floating-point value } { IEE 754 floating-point value }
AStream.ReadBuffer(AValue, 8); AStream.ReadBuffer(AValue, 8);
{ Save the data } { Save the data }
FWorksheet.WriteNumber(ARow, ACol, AValue); FWorksheet.WriteNumber(ARow, ACol, AValue);
{ Apply formatting to cell }
ApplyCellFormatting(ARow, ACol, XF, AFormat, AFont, AStyle);
end; end;
procedure TsSpreadBIFF2Reader.ReadInteger(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadInteger(AStream: TStream);
var var
ARow, ACol: Word; ARow, ACol: Word;
XF, AFormat, AFont, AStyle: Byte;
AWord : Word; AWord : Word;
begin begin
{ BIFF Record data } { BIFF Record row/column/style }
ARow := WordLEToN(AStream.ReadWord); ReadRowColStyle(AStream, ARow, ACol, XF, AFormat, AFont, AStyle);
ACol := WordLEToN(AStream.ReadWord);
{ BIFF2 Attributes }
AStream.ReadByte();
AStream.ReadByte();
AStream.ReadByte();
{ 16 bit unsigned integer } { 16 bit unsigned integer }
AStream.ReadBuffer(AWord, 2); AStream.ReadBuffer(AWord, 2);
{ Save the data } { Save the data }
FWorksheet.WriteNumber(ARow, ACol, AWord); FWorksheet.WriteNumber(ARow, ACol, AWord);
{ Apply formatting to cell }
ApplyCellFormatting(ARow, ACol, XF, AFormat, AFont, AStyle);
end;
procedure TsSpreadBIFF2Reader.ReadRowColStyle(AStream: TStream;
out ARow, ACol: Word; out XF, AFormat, AFont, AStyle: byte);
type
TRowColStyleRecord = packed record
Row, Col: Word;
XFIndex: Byte;
Format_Font: Byte;
Style: Byte;
end;
var
rcs: TRowColStyleRecord;
begin
AStream.ReadBuffer(rcs, SizeOf(TRowColStyleRecord));
ARow := WordLEToN(rcs.Row);
ACol := WordLEToN(rcs.Col);
XF := rcs.XFIndex;
AFormat := (rcs.Format_Font AND $3F);
AFont := (rcs.Format_Font AND $C0) shr 6;
AStyle := rcs.Style;
end; end;
procedure TsSpreadBIFF2Reader.ReadRowInfo(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadRowInfo(AStream: TStream);

View File

@ -83,8 +83,10 @@ type
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 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;
@ -109,17 +111,23 @@ type
WorkBookEncoding: TsEncoding; WorkBookEncoding: TsEncoding;
protected protected
{ Record writing methods } { Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
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 WriteCodepage(AStream: TStream; AEncoding: TsEncoding); this is in xlscommon
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
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: TFPCustomFont);
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override; procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
const AFormula: TsRPNFormula; ACell: PCell); override;
procedure WriteIndex(AStream: TStream); procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; const AValue: string; ACell: PCell); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: double; 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);
@ -135,6 +143,7 @@ implementation
const const
{ Excel record IDs } { Excel record IDs }
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_EOF = $000A; INT_EXCEL_ID_EOF = $000A;
@ -417,6 +426,27 @@ begin
SetLength(Boundsheets, 0); SetLength(Boundsheets, 0);
end; end;
{*******************************************************************
* TsSpreadBIFF5Writer.WriteBlank
*
* DESCRIPTION: Writes the record for an empty cell
*
*******************************************************************}
procedure TsSpreadBIFF5Writer.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell);
begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK));
AStream.WriteWord(WordToLE(6));
{ BIFF Record data }
AStream.WriteWord(WordToLE(ARow));
AStream.WriteWord(WordToLE(ACol));
{ Index to XF record }
AStream.WriteWord(WordToLE(15));
end;
{******************************************************************* {*******************************************************************
* TsSpreadBIFF5Writer.WriteBOF () * TsSpreadBIFF5Writer.WriteBOF ()
* *
@ -1125,9 +1155,9 @@ begin
case RecordType of case RecordType of
INT_EXCEL_ID_BLANK: ReadBlank(AStream);
INT_EXCEL_ID_NUMBER: ReadNumber(AStream); INT_EXCEL_ID_NUMBER: ReadNumber(AStream);
INT_EXCEL_ID_LABEL: ReadLabel(AStream); INT_EXCEL_ID_LABEL: ReadLabel(AStream);
// INT_EXCEL_ID_FORMULA: ReadFormula(AStream);
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);
@ -1230,6 +1260,9 @@ begin
AStream.ReadByte; // First formatted character AStream.ReadByte; // First formatted character
AStream.ReadByte; // Index to FONT record AStream.ReadByte; // Index to FONT record
end; end;
{ Add attributes to cell }
ApplyCellFormatting(ARow, ACol, XF);
end; end;
procedure TsSpreadBIFF5Reader.ReadRKValue(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadRKValue(AStream: TStream);
@ -1247,6 +1280,9 @@ begin
Number:=DecodeRKValue(L); Number:=DecodeRKValue(L);
FWorksheet.WriteNumber(ARow,ACol,Number); FWorksheet.WriteNumber(ARow,ACol,Number);
{ Add attributes to cell }
ApplyCellFormatting(ARow, ACol, XF);
end; end;
procedure TsSpreadBIFF5Reader.ReadMulRKValues(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadMulRKValues(AStream: TStream);
@ -1296,6 +1332,9 @@ begin
if SizeOf(Double)<>8 then Raise Exception.Create('Double is not 8 bytes'); if SizeOf(Double)<>8 then Raise Exception.Create('Double is not 8 bytes');
Move(Data[0],ResultFormula,sizeof(Data)); Move(Data[0],ResultFormula,sizeof(Data));
FWorksheet.WriteNumber(ARow,ACol,ResultFormula); FWorksheet.WriteNumber(ARow,ACol,ResultFormula);
{ Add attributes to cell }
ApplyCellFormatting(ARow, ACol, XF);
end; end;
function TsSpreadBIFF5Reader.DecodeRKValue(const ARK: DWORD): Double; function TsSpreadBIFF5Reader.DecodeRKValue(const ARK: DWORD): Double;
@ -1402,6 +1441,16 @@ begin
FWorksheetNames.Free; FWorksheetNames.Free;
end; end;
procedure TsSpreadBIFF5Reader.ReadBlank(AStream: TStream);
var
ARow, ACol, XF: Word;
begin
{ Read row, column, and XF index from BIFF file }
ReadRowColXF(AStream, ARow, ACol, XF);
{ Add attributes to cell}
ApplyCellFormatting(ARow, ACol, XF);
end;
procedure TsSpreadBIFF5Reader.ReadFormula(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadFormula(AStream: TStream);
begin begin
@ -1424,6 +1473,9 @@ begin
{ Save the data } { Save the data }
FWorksheet.WriteUTF8Text(ARow, ACol, ISO_8859_1ToUTF8(AStrValue)); FWorksheet.WriteUTF8Text(ARow, ACol, ISO_8859_1ToUTF8(AStrValue));
{ Add attributes }
ApplyCellFormatting(ARow, ACol, XF);
end; end;
procedure TsSpreadBIFF5Reader.ReadNumber(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadNumber(AStream: TStream);
@ -1438,8 +1490,18 @@ begin
{ Save the data } { Save the data }
FWorksheet.WriteNumber(ARow, ACol, AValue); FWorksheet.WriteNumber(ARow, ACol, AValue);
{ Add attributes to cell }
ApplyCellFormatting(ARow, ACol, XF);
end; end;
procedure TsSpreadBIFF5Reader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Integer);
begin
// to do...
end;
initialization initialization
RegisterSpreadFormat(TsSpreadBIFF5Reader, TsSpreadBIFF5Writer, sfExcel5); RegisterSpreadFormat(TsSpreadBIFF5Reader, TsSpreadBIFF5Writer, sfExcel5);

View File

@ -136,16 +136,17 @@ type
procedure ReadFont(const AStream: TStream); procedure ReadFont(const AStream: TStream);
// Read col info // Read col info
procedure ReadColInfo(const AStream: TStream); 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;
public public
constructor Create; override; constructor Create; override;
destructor Destroy; override; destructor Destroy; override;
{ General reading methods } { General reading methods }
procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override; procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override;
procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); override; procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); override;
{ Record writing methods }
procedure ReadFormula(AStream: TStream); override;
procedure ReadLabel(AStream: TStream); override;
procedure ReadNumber(AStream: TStream); override;
end; end;
{ TsSpreadBIFF8Writer } { TsSpreadBIFF8Writer }
@ -160,21 +161,28 @@ type
protected protected
procedure AddDefaultFormats(); override; procedure AddDefaultFormats(); override;
{ Record writing methods } { Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
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 WriteCodepage in xlscommon; Workbook Globals record
procedure WriteColInfo(AStream: TStream; ASheet: TsWorksheet; ACol: PCol); procedure WriteColInfo(AStream: TStream; ASheet: TsWorksheet; ACol: PCol);
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override;
// procedure WriteDateMode in xlscommon; Workbook Globals record // 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: TFPCustomFont); procedure WriteFont(AStream: TStream; AFont: TFPCustomFont);
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); override; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
const AFormula: TsFormula; ACell: PCell); override;
procedure WriteIndex(AStream: TStream); procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; const AValue: string; ACell: PCell); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: double; ACell: PCell); override;
procedure WritePalette(AStream: TStream); procedure WritePalette(AStream: TStream);
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; 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);
@ -196,6 +204,7 @@ implementation
const const
{ Excel record IDs } { Excel record IDs }
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_COLINFO = $007D;
@ -725,6 +734,28 @@ begin
SetLength(Boundsheets, 0); SetLength(Boundsheets, 0);
end; end;
{*******************************************************************
* TsSpreadBIFF8Writer.WriteBlank
*
* DESCRIPTION: Writes the record for an empty cell
*
*******************************************************************}
procedure TsSpreadBIFF8Writer.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell);
begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK));
AStream.WriteWord(WordToLE(6));
{ BIFF Record data }
AStream.WriteWord(WordToLE(ARow));
AStream.WriteWord(WordToLE(ACol));
{ Index to XF record, according to formatting }
WriteXFIndex(AStream, ACell);
end;
{******************************************************************* {*******************************************************************
* TsSpreadBIFF8Writer.WriteBOF () * TsSpreadBIFF8Writer.WriteBOF ()
* *
@ -1955,6 +1986,7 @@ begin
case RecordType of case RecordType of
INT_EXCEL_ID_BLANK: ReadBlank(AStream);
INT_EXCEL_ID_NUMBER: ReadNumber(AStream); INT_EXCEL_ID_NUMBER: ReadNumber(AStream);
INT_EXCEL_ID_LABEL: ReadLabel(AStream); INT_EXCEL_ID_LABEL: ReadLabel(AStream);
INT_EXCEL_ID_FORMULA: ReadFormula(AStream); INT_EXCEL_ID_FORMULA: ReadFormula(AStream);
@ -2094,7 +2126,7 @@ var
lCell: PCell; lCell: PCell;
XFData: TXFRecordData; XFData: TXFRecordData;
begin begin
lCell := FWorksheet.FindCell(ARow, ACol); lCell := FWorksheet.GetCell(ARow, ACol);
if Assigned(lCell) then begin if Assigned(lCell) then begin
XFData := TXFRecordData(FXFList.Items[XFIndex]); XFData := TXFRecordData(FXFList.Items[XFIndex]);
@ -2212,6 +2244,16 @@ begin
FWorksheetNames.Free; FWorksheetNames.Free;
end; end;
procedure TsSpreadBIFF8Reader.ReadBlank(AStream: TStream);
var
ARow, ACol, XF: Word;
begin
{ Read row, column, and XF index from BIFF file }
ReadRowColXF(AStream, ARow, ACol, XF);
{ Add attributes to cell}
ApplyCellFormatting(ARow, ACol, XF);
end;
procedure TsSpreadBIFF8Reader.ReadFormula(AStream: TStream); procedure TsSpreadBIFF8Reader.ReadFormula(AStream: TStream);
var var
ARow, ACol, XF: WORD; ARow, ACol, XF: WORD;

View File

@ -283,7 +283,6 @@ type
protected protected
FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding
FDateMode: TDateMode; FDateMode: TDateMode;
constructor Create; override;
// converts an Excel color index to a color value. // converts an Excel color index to a color value.
function ExcelPaletteToFPSColor(AIndex: Word): TsColor; function ExcelPaletteToFPSColor(AIndex: Word): TsColor;
// 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 BIFF2-8 versions
@ -293,6 +292,8 @@ type
procedure ReadDateMode(AStream: TStream); procedure ReadDateMode(AStream: TStream);
// Read row info // Read row info
procedure ReadRowInfo(const AStream: TStream); virtual; procedure ReadRowInfo(const AStream: TStream); virtual;
public
constructor Create; override;
end; end;
{ TsSpreadBIFFWriter } { TsSpreadBIFFWriter }