fpspreadsheet: Massive reconstruction: remove formatting attributes of TCell record and collect in TCellFormatList. TCell only has index into this list. Introduce record helpers to keep the old syntax. Reduction of memory consumption per cell by 50%.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3894 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-01-23 21:54:23 +00:00
parent 7793cf2063
commit 133bf09eb4
43 changed files with 3491 additions and 1437 deletions

View File

@@ -21,6 +21,7 @@ var
lRow: TRow;
r: Integer;
fmt: String;
begin
// Open the output file
MyDir := ExtractFilePath(ParamStr(0));

View File

@@ -10,7 +10,7 @@ program excel8write;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpspreadsheet, fpsRPN, xlsbiff8, fpsTypes;
Classes, SysUtils, fpspreadsheet, fpsRPN, xlsbiff8, fpsTypes, fpsHelpers;
const
Str_First = 'First';
@@ -66,7 +66,7 @@ begin
MyWorksheet.WriteNumber(5, 3, 10);
lCell := MyWorksheet.GetCell(5, 3);
lCell^.BackgroundColor := scPurple;
lCell^.UsedFormattingFields := [uffBackgroundColor];
// lCell^.UsedFormattingFields := [uffBackgroundColor];
// or: MyWorksheet.WriteBackgroundColor(5, 3, scPurple);
MyWorksheet.WriteFontColor(5, 3, scWhite);
MyWorksheet.WriteFontSize(5, 3, 12);

View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="9"/>
@@ -40,9 +40,6 @@
</Win32>
</Options>
</Linking>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
</Item2>
</BuildModes>
@@ -100,9 +97,6 @@
</Win32>
</Options>
</Linking>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="9"/>
@@ -40,9 +40,6 @@
</Win32>
</Options>
</Linking>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
</Item2>
</BuildModes>
@@ -59,7 +56,7 @@
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="2">
<Units Count="3">
<Unit0>
<Filename Value="fpsgrid.lpr"/>
<IsPartOfProject Value="True"/>
@@ -72,6 +69,10 @@
<ResourceBaseClass Value="Form"/>
<UnitName Value="mainfrm"/>
</Unit1>
<Unit2>
<Filename Value="..\..\fpshelpers.pas"/>
<IsPartOfProject Value="True"/>
</Unit2>
</Units>
</ProjectOptions>
<CompilerOptions>
@@ -98,9 +99,6 @@
</Win32>
</Options>
</Linking>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">

View File

@@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, mainfrm
Forms, mainfrm, fpsHelpers
{ you can add units after this };
{$R *.res}

View File

@@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
ComCtrls, StdCtrls, Grids,
fpSpreadsheet, fpspreadsheetgrid, {%H-}fpsallformats;
fpspreadsheet, fpspreadsheetgrid, {%H-}fpsallformats;
type

View File

@@ -25,7 +25,7 @@ begin
// Open the input file
MyDir := ExtractFilePath(ParamStr(0));
InputFileName := MyDir + 'test.xlsx';
InputFileName := MyDir + 'a.xlsx';
if not FileExists(InputFileName) then begin
WriteLn('Input file ', InputFileName, ' does not exist. Please run opendocwrite first.');
Halt;
@@ -56,5 +56,7 @@ begin
// Finalization
MyWorkbook.Free;
ReadLn;
end.

View File

@@ -10,7 +10,7 @@ program ooxmlwrite;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpstypes, fpspreadsheet, fpsallformats;
Classes, SysUtils, fpstypes, fpspreadsheet, fpsallformats, fpshelpers;
var
MyWorkbook: TsWorkbook;

View File

@@ -42,6 +42,7 @@
<Unit0>
<Filename Value="demo_write_formatting.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="demo_write_formatting"/>
</Unit0>
</Units>
</ProjectOptions>

View File

@@ -12,7 +12,8 @@ program demo_write_formatting;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpsTypes, fpspreadsheet, xlsbiff8, fpsopendocument;
Classes, SysUtils,
fpsTypes, fpspreadsheet, xlsbiff8, fpsopendocument, fpscell;
var
MyWorkbook: TsWorkbook;
@@ -30,83 +31,67 @@ begin
MyWorksheet.WriteUTF8Text(1, 1, '[]'); // B2
MyCell := MyWorksheet.GetCell(1, 1);
MyCell^.Border := [];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(1, 3, '[N]');// D2
MyCell := MyWorksheet.GetCell(1, 3);
MyCell^.Border := [cbNorth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(1, 5, '[W]');// F2
MyCell := MyWorksheet.GetCell(1, 5);
MyCell^.Border := [cbWest];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(1, 7, '[E]');// H2
MyCell := MyWorksheet.GetCell(1, 7);
MyCell^.Border := [cbEast];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(1, 9, '[S]');// J2
MyCell := MyWorksheet.GetCell(1, 9);
MyCell^.Border := [cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(3, 1, '[N,W]');// B4
MyCell := MyWorksheet.GetCell(3, 1);
MyCell^.Border := [cbNorth, cbWest];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(3, 3, '[N,E]');// D4
MyCell := MyWorksheet.GetCell(3, 3);
MyCell^.Border := [cbNorth, cbEast];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(3, 5, '[N,S]');// F4
MyCell := MyWorksheet.GetCell(3, 5);
MyCell^.Border := [cbNorth, cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(3, 7, '[W,E]');// H4
MyCell := MyWorksheet.GetCell(3, 7);
MyCell^.Border := [cbWest, cbEast];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(3, 9, '[W,S]');// J4
MyCell := MyWorksheet.GetCell(3, 9);
MyCell^.Border := [cbWest, cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(3, 11, '[E,S]');// L4
MyCell := MyWorksheet.GetCell(3, 11);
MyCell^.Border := [cbEast, cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(5, 1, '[N,W,E]');// B6
MyCell := MyWorksheet.GetCell(5, 1);
MyCell^.Border := [cbNorth, cbWest, cbEast];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(5, 3, '[N,W,S]');// D6
MyCell := MyWorksheet.GetCell(5, 3);
MyCell^.Border := [cbNorth, cbWest, cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(5, 5, '[N,E,S]');// F6
MyCell := MyWorksheet.GetCell(5, 5);
MyCell^.Border := [cbNorth, cbEast, cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(5, 7, '[W,E,S]');// H6
MyCell := MyWorksheet.GetCell(5, 7);
MyCell^.Border := [cbWest, cbEast, cbSouth];
MyCell^.UsedFormattingFields := [uffBorder];
MyWorksheet.WriteUTF8Text(5, 9, '[N,W,E,S]');// J6
MyCell := MyWorksheet.GetCell(5, 9);
MyCell^.Border := [cbNorth, cbWest, cbEast, cbSouth];
MyCell^.BackgroundColor := scGreen;
MyCell^.UsedFormattingFields := [uffBorder, uffBold, uffBackgroundColor];
end;
procedure WriteSecondWorksheet();
@@ -121,13 +106,11 @@ begin
MyCell := MyWorksheet.GetCell(1, 1);
MyCell^.Border := [cbNorth, cbWest, cbSouth];
MyCell^.BackgroundColor := scGrey20pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor, uffBold];
MyWorksheet.WriteUTF8Text(1, 2, ' ');
MyCell := MyWorksheet.GetCell(1, 2);
MyCell^.Border := [cbNorth, cbEast, cbSouth];
MyCell^.BackgroundColor := scGrey20pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor];
// Line 2
@@ -135,13 +118,11 @@ begin
MyCell := MyWorksheet.GetCell(2, 1);
MyCell^.Border := [cbWest];
MyCell^.BackgroundColor := scGrey10pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor];
MyWorksheet.WriteUTF8Text(2, 2, 'R$ 20');
MyCell := MyWorksheet.GetCell(2, 2);
MyCell^.Border := [cbEast];
MyCell^.BackgroundColor := scGrey10pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor];
// Line 3
@@ -149,13 +130,11 @@ begin
MyCell := MyWorksheet.GetCell(3, 1);
MyCell^.Border := [cbWest, cbSouth];
MyCell^.BackgroundColor := scGrey10pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor];
MyWorksheet.WriteUTF8Text(3, 2, 'R$ 20');
MyCell := MyWorksheet.GetCell(3, 2);
MyCell^.Border := [cbEast, cbSouth];
MyCell^.BackgroundColor := scGrey10pct;
MyCell^.UsedFormattingFields := [uffBorder, uffBackgroundColor];
end;
const
@@ -171,7 +150,6 @@ begin
MyWorkbook := TsWorkbook.Create;
WriteFirstWorksheet();
WriteSecondWorksheet();
// Save the spreadsheet to a file

View File

@@ -8,7 +8,8 @@ uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, Menus, ExtCtrls, ComCtrls, ActnList, Spin, Grids,
ColorBox, ValEdit,
fpstypes, fpspreadsheetgrid, fpspreadsheet, {%H-}fpsallformats;
fpstypes, fpspreadsheetgrid, fpspreadsheet,
{%H-}fpsallformats;
type
@@ -617,6 +618,8 @@ var
cell: PCell;
decs: Byte;
currsym: String;
nf: TsNumberFormat;
nfs: String;
begin
currsym := Sender.ClassName;
with WorksheetGrid do begin
@@ -624,7 +627,8 @@ begin
exit;
cell := Worksheet.FindCell(GetWorksheetRow(Row), GetWorksheetCol(Col));
if (cell <> nil) then begin
if cell^.NumberFormat = nfGeneral then begin
Worksheet.ReadNumFormat(cell, nf, nfs);
if nf = nfGeneral then begin
Worksheet.WriteNumberFormat(cell, nfFixed, '0.00');
exit;
end;
@@ -667,13 +671,14 @@ procedure TMainFrm.AcNumFormatExecute(Sender: TObject);
const
DATETIME_CUSTOM: array[0..4] of string = ('', 'dd/mmm', 'mmm/yy', 'nn:ss', 'nn:ss.zzz');
var
nf: TsNumberFormat;
c, r: Cardinal;
cell: PCell;
fmt: String;
decs: Byte;
cs: String;
isDateTimeFmt: Boolean;
nf, cell_nf: TsNumberFormat;
cell_nfs: String;
begin
if TAction(Sender).Checked then
nf := TsNumberFormat((TAction(Sender).Tag - NUMFMT_TAG) div 10)
@@ -691,23 +696,24 @@ begin
c := GetWorksheetCol(Col);
r := GetWorksheetRow(Row);
cell := Worksheet.GetCell(r, c);
Worksheet.ReadNumFormat(cell, cell_nf, cell_nfs);
Worksheet.GetNumberFormatAttributes(cell, decs, cs);
if cs = '' then cs := '?';
case cell^.ContentType of
cctNumber, cctDateTime:
if isDateTimeFmt then begin
if IsDateTimeFormat(cell^.NumberFormat) then
if IsDateTimeFormat(cell_nf) then
Worksheet.WriteDateTime(cell, cell^.DateTimeValue, nf, fmt)
else
Worksheet.WriteDateTime(cell, cell^.NumberValue, nf, fmt);
end else
if IsCurrencyFormat(nf) then begin
if IsDateTimeFormat(cell^.NumberFormat) then
if IsDateTimeFormat(cell_nf) then
Worksheet.WriteCurrency(cell, cell^.DateTimeValue, nf, decs, cs)
else
Worksheet.WriteCurrency(cell, cell^.Numbervalue, nf, decs, cs);
end else begin
if IsDateTimeFormat(cell^.NumberFormat) then
if IsDateTimeFormat(cell_nf) then
Worksheet.WriteNumber(cell, cell^.DateTimeValue, nf, decs)
else
Worksheet.WriteNumber(cell, cell^.NumberValue, nf, decs)
@@ -1114,6 +1120,7 @@ var
s: String;
cb: TsCellBorder;
r1,r2,c1,c2: Cardinal;
fmt: TsCellFormat;
begin
with CellInspector do
begin
@@ -1174,30 +1181,33 @@ begin
end
else
begin
if (ACell=nil) or not (uffFont in ACell^.UsedFormattingFields)
if ACell <> nil
then fmt := WorksheetGrid.Workbook.GetCellFormat(ACell^.FormatIndex)
else InitFormatRecord(fmt);
if (ACell=nil) or not (uffFont in fmt.UsedFormattingFields)
then Strings.Add('FontIndex=')
else Strings.Add(Format('FontIndex=%d (%s(', [
ACell^.FontIndex,
WorksheetGrid.Workbook.GetFontAsString(ACell^.FontIndex)]));
if (ACell=nil) or not (uffTextRotation in ACell^.UsedFormattingFields)
else Strings.Add(Format('FontIndex=%d (%s)', [
fmt.FontIndex,
WorksheetGrid.Workbook.GetFontAsString(fmt.FontIndex)]));
if (ACell=nil) or not (uffTextRotation in fmt.UsedFormattingFields)
then Strings.Add('TextRotation=')
else Strings.Add(Format('TextRotation=%s', [GetEnumName(TypeInfo(TsTextRotation), ord(ACell^.TextRotation))]));
if (ACell=nil) or not (uffHorAlign in ACell^.UsedFormattingFields)
else Strings.Add(Format('TextRotation=%s', [GetEnumName(TypeInfo(TsTextRotation), ord(fmt.TextRotation))]));
if (ACell=nil) or not (uffHorAlign in fmt.UsedFormattingFields)
then Strings.Add('HorAlignment=')
else Strings.Add(Format('HorAlignment=%s', [GetEnumName(TypeInfo(TsHorAlignment), ord(ACell^.HorAlignment))]));
if (ACell=nil) or not (uffVertAlign in ACell^.UsedFormattingFields)
else Strings.Add(Format('HorAlignment=%s', [GetEnumName(TypeInfo(TsHorAlignment), ord(fmt.HorAlignment))]));
if (ACell=nil) or not (uffVertAlign in fmt.UsedFormattingFields)
then Strings.Add('VertAlignment=')
else Strings.Add(Format('VertAlignment=%s', [GetEnumName(TypeInfo(TsVertAlignment), ord(ACell^.VertAlignment))]));
if (ACell=nil) or not (uffBorder in ACell^.UsedFormattingFields) then
else Strings.Add(Format('VertAlignment=%s', [GetEnumName(TypeInfo(TsVertAlignment), ord(fmt.VertAlignment))]));
if (ACell=nil) or not (uffBorder in fmt.UsedFormattingFields) then
Strings.Add('Borders=')
else begin
s := '';
if cbNorth in ACell^.Border then s := s + ', cbNorth';
if cbSouth in ACell^.Border then s := s + ', cbSouth';
if cbEast in ACell^.Border then s := s + ', cbEast';
if cbWest in ACell^.Border then s := s + ', cbWest';
if cbDiagUp in ACell^.Border then s := s + ', cbDiagUp';
if cbDiagDown in ACell^.Border then s := s + ', cbDiagDown';
if cbNorth in fmt.Border then s := s + ', cbNorth';
if cbSouth in fmt.Border then s := s + ', cbSouth';
if cbEast in fmt.Border then s := s + ', cbEast';
if cbWest in fmt.Border then s := s + ', cbWest';
if cbDiagUp in fmt.Border then s := s + ', cbDiagUp';
if cbDiagDown in fmt.Border then s := s + ', cbDiagDown';
if s <> '' then Delete(s, 1, 2);
Strings.Add('Borders='+s);
end;
@@ -1209,21 +1219,21 @@ begin
else
Strings.Add(Format('BorderStyles[%s]=%s, %s', [
GetEnumName(TypeInfo(TsCellBorder), ord(cb)),
GetEnumName(TypeInfo(TsLineStyle), ord(ACell^.BorderStyles[cbEast].LineStyle)),
WorksheetGrid.Workbook.GetColorName(ACell^.BorderStyles[cbEast].Color)
GetEnumName(TypeInfo(TsLineStyle), ord(fmt.BorderStyles[cbEast].LineStyle)),
WorksheetGrid.Workbook.GetColorName(fmt.BorderStyles[cbEast].Color)
]));
if (ACell=nil) or not (uffBackgroundColor in ACell^.UsedformattingFields)
if (ACell=nil) or not (uffBackgroundColor in fmt.UsedformattingFields)
then Strings.Add('BackgroundColor=')
else Strings.Add(Format('BackgroundColor=%d (%s)', [
ACell^.BackgroundColor,
WorksheetGrid.Workbook.GetColorName(Acell^.BackgroundColor)
fmt.BackgroundColor,
WorksheetGrid.Workbook.GetColorName(fmt.BackgroundColor)
]));
if (ACell=nil) or not (uffNumberFormat in ACell^.UsedFormattingFields)
if (ACell=nil) or not (uffNumberFormat in fmt.UsedFormattingFields)
then Strings.Add('NumberFormat=')
else Strings.Add(Format('NumberFormat=%s', [GetEnumName(TypeInfo(TsNumberFormat), ord(ACell^.NumberFormat))]));
if (ACell=nil) or not (uffNumberFormat in ACell^.UsedFormattingFields)
else Strings.Add(Format('NumberFormat=%s', [GetEnumName(TypeInfo(TsNumberFormat), ord(fmt.NumberFormat))]));
if (ACell=nil) or not (uffNumberFormat in fmt.UsedFormattingFields)
then Strings.Add('NumberFormatStr=')
else Strings.Add('NumberFormatStr=' + ACell^.NumberFormatStr);
else Strings.Add('NumberFormatStr=' + fmt.NumberFormatStr);
if not WorksheetGrid.Worksheet.IsMerged(ACell) then
Strings.Add('Merged range=')
else
@@ -1285,6 +1295,7 @@ var
i: Integer;
ac: TAction;
nf: TsNumberFormat;
nfs: String;
cell: PCell;
r,c: Cardinal;
found: Boolean;
@@ -1296,17 +1307,17 @@ begin
if (cell = nil) or not (cell^.ContentType in [cctNumber, cctDateTime]) then
nf := nfGeneral
else
nf := cell^.NumberFormat;
Worksheet.ReadNumFormat(cell, nf, nfs);
for i:=0 to ActionList.ActionCount-1 do begin
ac := TAction(ActionList.Actions[i]);
if (ac.Tag >= NUMFMT_TAG) and (ac.Tag < NUMFMT_TAG + 200) then begin
found := ((ac.Tag - NUMFMT_TAG) div 10 = ord(nf));
if nf = nfCustom then
case (ac.Tag - NUMFMT_TAG) mod 10 of
1: found := cell^.NumberFormatStr = 'dd/mmm';
2: found := cell^.NumberFormatStr = 'mmm/yy';
3: found := cell^.NumberFormatStr = 'nn:ss';
4: found := cell^.NumberFormatStr = 'nn:ss.z';
1: found := nfs = 'dd/mmm';
2: found := nfs = 'mmm/yy';
3: found := nfs = 'nn:ss';
4: found := nfs = 'nn:ss.z';
end;
ac.Checked := found;
end;

View File

@@ -116,7 +116,6 @@
<Unit3>
<Filename Value="sctrls.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="sCtrls"/>
</Unit3>
<Unit4>
<Filename Value="sformatsettingsform.pas"/>
@@ -140,12 +139,10 @@
<ComponentName Value="CurrencyForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="sfCurrencyForm"/>
</Unit6>
<Unit7>
<Filename Value="..\..\fpscurrency.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="fpsCurrency"/>
</Unit7>
</Units>
</ProjectOptions>

View File

@@ -10,6 +10,13 @@
{.$DEFINE FPS_DONT_USE_CLOCALE}
{ In older versions of fpspreadsheet, the formatting fields had belonged to the
cell record. This has been given up to reduce memory consumption.
For fpc >2.6, however, record helpers allow to get this feature back. In case
of older compilers activate the define FPS_NO_RECORD_HELPERS. Note that worksheet
methods can only be used to change cell formatting then. }
{.$DEFINE FPS_NO_RECORD_HELPERS}
{ The next defines activate code duplicated from new compiler versions in case
an old compiler is used. }

View File

@@ -771,7 +771,7 @@ var
fnt: TsFont;
fs: TsFontStyles;
begin
fnt := Workbook.GetFont(ACell^.FontIndex);
fnt := Worksheet.ReadCellFont(ACell);
fs := fnt.Style;
if Checked then
Include(fs, FFontStyle)
@@ -783,16 +783,21 @@ end;
procedure TsFontStyleAction.ExtractFromCell(ACell: PCell);
var
fnt: TsFont;
fmt: PsCellFormat;
begin
if (ACell = nil) then
Checked := false
else
if (uffBold in ACell^.UsedFormattingFields) then
begin
Checked := false;
exit;
end;
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
if (uffBold in fmt^.UsedFormattingFields) then
Checked := (FFontStyle = fssBold)
else
if (uffFont in ACell^.UsedFormattingFields) then
if (uffFont in fmt^.UsedFormattingFields) then
begin
fnt := Workbook.GetFont(ACell^.FontIndex);
fnt := Workbook.GetFont(fmt^.FontIndex);
Checked := (FFontStyle in fnt.Style);
end else
Checked := false;
@@ -818,10 +823,7 @@ end;
procedure TsHorAlignmentAction.ExtractFromCell(ACell: PCell);
begin
if (ACell = nil) or not (uffHorAlign in ACell^.UsedFormattingFields) then
Checked := false
else
Checked := ACell^.HorAlignment = FHorAlign;
Checked := (ACell <> nil) and (Worksheet.ReadHorAlignment(ACell) = FHorAlign);
end;
@@ -844,10 +846,7 @@ end;
procedure TsVertAlignmentAction.ExtractFromCell(ACell: PCell);
begin
if (ACell = nil) or not (uffVertAlign in ACell^.UsedFormattingFields) then
Checked := false
else
Checked := ACell^.VertAlignment = FVertAlign;
Checked := (ACell <> nil) and (Worksheet.ReadVertAlignment(ACell) = FVertAlign);
end;
@@ -870,10 +869,7 @@ end;
procedure TsTextRotationAction.ExtractFromCell(ACell: PCell);
begin
if (ACell = nil) or not (uffTextRotation in ACell^.UsedFormattingFields) then
Checked := false
else
Checked := ACell^.TextRotation = FTextRotation;
Checked := (ACell <> nil) and (Worksheet.ReadTextRotation(ACell) = FTextRotation);
end;
@@ -892,7 +888,7 @@ end;
procedure TsWordwrapAction.ExtractFromCell(ACell: PCell);
begin
Checked := (ACell <> nil) and (uffWordwrap in ACell^.UsedFormattingFields);
Checked := (ACell <> nil) and Worksheet.ReadWordwrap(ACell);
end;
function TsWordwrapAction.GetWordwrap: Boolean;
@@ -936,12 +932,12 @@ begin
end;
procedure TsNumberFormatAction.ExtractFromCell(ACell: PCell);
var
nf: TsNumberFormat;
nfs: String;
begin
if (ACell = nil) or not (uffNumberFormat in ACell^.UsedFormattingFields) then
Checked := false
else
Checked := (ACell^.NumberFormat = FNumberFormat)
and (ACell^.NumberFormatStr = FNumberFormatStr);
Worksheet.ReadNumFormat(ACell, nf, nfs);
Checked := (ACell <> nil) and (nf = FNumberFormat) and (nfs = FNumberFormatStr);
end;
@@ -956,14 +952,15 @@ end;
procedure TsDecimalsAction.ApplyFormatToCell(ACell: PCell);
var
decs: Integer;
nf: TsNumberFormat;
nfs: String;
begin
if IsDateTimeFormat(ACell^.NumberFormat) then
Worksheet.ReadNumFormat(ACell, nf, nfs);
if IsDateTimeFormat(nf) then
exit;
if (ACell^.ContentType in [cctEmpty, cctNumber]) and (
(not (uffNumberFormat in ACell^.UsedFormattingFields)) or
(ACell^.NumberFormat = nfGeneral)
) then
if (ACell^.ContentType in [cctEmpty, cctNumber]) and (nf <> nfGeneral) then
decs := Worksheet.GetDisplayedDecimals(ACell)
else
decs := FDecimals;
@@ -976,16 +973,16 @@ procedure TsDecimalsAction.ExtractFromCell(ACell: PCell);
var
csym: String;
decs: Byte;
nf: TsNumberFormat;
nfs: String;
begin
if ACell = nil then begin
FDecimals := 2;
exit;
end;
if (ACell^.ContentType in [cctEmpty, cctNumber]) and (
(not (uffNumberFormat in ACell^.UsedFormattingFields)) or
(ACell^.NumberFormat = nfGeneral)
) then
Worksheet.ReadNumFormat(ACell, nf, nfs);
if (ACell^.ContentType in [cctEmpty, cctNumber]) and (nf <> nfGeneral) then
decs := Worksheet.GetDisplayedDecimals(ACell)
else
Worksheet.GetNumberFormatAttributes(ACell, decs, csym);
@@ -1029,8 +1026,11 @@ end;
procedure TsActionBorders.ExtractFromCell(AWorkbook: TsWorkbook; ACell: PCell);
var
cb: TsCellBorder;
fmt: PsCellFormat;
begin
if (ACell = nil) or not (uffBorder in ACell^.UsedFormattingFields) then
if (ACell <> nil) then
fmt := AWorkbook.GetPointerToCellFormat(ACell^.FormatIndex);
if (ACell = nil) or not (uffBorder in fmt^.UsedFormattingFields) then
for cb in TsCellBorder do
begin
FBorders[cb].ExtractStyle(AWorkbook, DEFAULT_BORDERSTYLES[cb]);
@@ -1039,8 +1039,8 @@ begin
else
for cb in TsCellBorder do
begin
FBorders[cb].ExtractStyle(AWorkbook, ACell^.BorderStyles[cb]);
FBorders[cb].Visible := cb in ACell^.Border;
FBorders[cb].ExtractStyle(AWorkbook, fmt^.BorderStyles[cb]);
FBorders[cb].Visible := cb in fmt^.Border;
end;
end;
@@ -1072,16 +1072,18 @@ procedure TsCellBorderAction.ApplyFormatToRange(ARange: TsCellRange);
procedure ShowBorder(ABorder: TsCellBorder; ACell: PCell;
ABorderStyle: TsCellBorderStyle; AEnable: boolean);
var
brdr: TsCellBorders;
fmt: TsCellFormat;
begin
brdr := ACell^.Border;
fmt := Workbook.GetCellFormat(ACell^.FormatIndex);
if AEnable then
begin
Include(brdr, ABorder);
Worksheet.WriteBorderStyle(ACell, ABorder, ABorderStyle);
Worksheet.WriteBorders(ACell, brdr);
// Don't modify the cell directly, this will miss the OnChange event.
end;
Include(fmt.Border, ABorder);
fmt.BorderStyles[ABorder] := ABorderStyle;
ACell^.FormatIndex := Workbook.AddCellFormat(fmt);
Include(fmt.UsedFormattingFields, uffBorder);
end else
Exclude(fmt.UsedFormattingFields, uffBorder);
Worksheet.ChangedCell(ACell^.Row, ACell^.Col);
end;
var
@@ -1139,7 +1141,8 @@ procedure TsCellBorderAction.ExtractFromCell(ACell: PCell);
var
EmptyCell: TCell;
begin
if (ACell = nil) or not (uffBorder in ACell^.UsedFormattingFields) then
// if (ACell = nil) or not (uffBorder in ACell^.UsedFormattingFields) then
if (ACell = nil) or not (uffBorder in Worksheet.ReadUsedFormatting(ACell)) then
begin
EmptyCell.Row := 0; // silence the compiler...
InitCell(EmptyCell);
@@ -1288,19 +1291,22 @@ procedure TsFontDialogAction.ExtractFromCell(ACell: PCell);
var
sfnt: TsFont;
fnt: TFont;
fmt: PsCellFormat;
begin
fnt := TFont.Create;
try
if (ACell = nil) then
sfnt := Workbook.GetDefaultFont
else
if uffBold in ACell^.UsedFormattingFields then
sfnt := Workbook.GetFont(1)
else
if uffFont in ACell^.UsedFormattingFields then
sfnt := Workbook.GetFont(ACell^.FontIndex)
else
sfnt := Workbook.GetDefaultFont;
else begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
if (uffBold in fmt^.UsedFormattingFields) then
sfnt := Workbook.GetFont(1)
else
if (uffFont in fmt^.UsedFormattingFields) then
sfnt := Workbook.GetFont(fmt^.FontIndex)
else
sfnt := Workbook.GetDefaultFont;
end;
Convert_sFont_to_Font(Workbook, sfnt, fnt);
GetDialog.Font.Assign(fnt);
finally
@@ -1339,11 +1345,15 @@ begin
end;
procedure TsBackgroundColorDialogAction.ExtractFromCell(ACell: PCell);
var
fmt: PsCellFormat;
begin
if (ACell = nil) or not (uffBackgroundColor in ACell^.UsedFormattingFields) then
FBackgroundColor := scNotDefined
else
FBackgroundColor := ACell^.BackgroundColor;
FBackgroundColor := scNotDefined;
if (ACell <> nil) then begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
if (uffBackgroundColor in fmt^.UsedFormattingFields) then
FBackgroundColor := fmt^.BackgroundColor;
end;
end;
function TsBackgroundColorDialogAction.GetDialog: TColorDialog;

View File

@@ -0,0 +1,146 @@
unit fpsCell;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
interface
uses
Classes, SysUtils, fpstypes, fpspreadsheet;
type
TCellHelper = record helper for TCell
private
function GetBackgroundColor: TsColor;
function GetBorder: TsCellBorders;
function GetBorderStyle(ABorder: TsCellBorder): TsCellBorderStyle;
function GetCellFormat: TsCellFormat;
function GetFont: TsFont;
function GetFontIndex: integer;
function GetHorAlignment: TsHorAlignment;
function GetNumberFormat: TsNumberFormat;
function GetNumberFormatStr: String;
function GetTextRotation: TsTextRotation;
function GetVertAlignment: TsVertAlignment;
function GetWordwrap: Boolean;
procedure SetBackgroundColor(AValue: TsColor);
procedure SetBorder(AValue: TsCellBorders);
procedure SetBorderStyle(ABorder: TsCellBorder; AValue: TsCellBorderStyle);
procedure SetFontIndex(AValue: Integer);
protected
function GetWorkbook: TsWorkbook;
public
property BackgroundColor: TsColor read GetBackgroundColor write SetBackgroundColor;
property Border: TsCellBorders read GetBorder write SetBorder;
property CellFormat: TsCellFormat read GetCellFormat;
property Font: TsFont read GetFont;
property FontIndex: Integer read GetFontIndex write SetFontIndex;
property HorAlignment: TsHorAlignment read GetHorAlignment;
property NumberFormat: TsNumberFormat read GetNumberFormat;
property NumberFormatStr: String read GetNumberFormatStr;
property TextRotation: TsTextRotation read GetTextRotation;
property VertAlignment: TsVertAlignment read GetVertAlignment;
property Wordwrap: Boolean read GetWordwrap;
property Workbook: TsWorkbook read GetWorkbook;
end;
implementation
function TCellHelper.GetBackgroundColor: TsColor;
begin
Result := Worksheet.ReadBackgroundColor(@self);
end;
function TCellHelper.GetBorder: TsCellBorders;
begin
Result := Worksheet.ReadCellBorders(@self);
end;
function TCellHelper.GetBorderStyle(ABorder: TsCellBorder): TsCellBorderStyle;
begin
Result := Worksheet.ReadCellBorderStyle(@self, ABorder);
end;
function TCellHelper.GetCellFormat: TsCellFormat;
begin
Result := Workbook.GetCellFormat(FormatIndex);
end;
function TCellHelper.GetFont: TsFont;
begin
Result := Worksheet.ReadCellFont(@self);
end;
function TCellHelper.GetFontIndex: Integer;
var
fmt: PsCellFormat;
begin
fmt := Workbook.GetPointerToCellFormat(FormatIndex);
Result := fmt^.FontIndex;
end;
function TCellHelper.GetHorAlignment: TsHorAlignment;
begin
Result := Worksheet.ReadHorAlignment(@Self);
end;
function TCellHelper.GetNumberFormat: TsNumberFormat;
var
fmt: PsCellFormat;
begin
fmt := Workbook.GetPointerToCellFormat(FormatIndex);
Result := fmt^.NumberFormat;
end;
function TCellHelper.GetNumberFormatStr: String;
var
fmt: PsCellFormat;
begin
fmt := Workbook.GetPointerToCellFormat(FormatIndex);
Result := fmt^.NumberFormatStr;
end;
function TCellHelper.GetTextRotation: TsTextRotation;
begin
Result := Worksheet.ReadTextRotation(@Self);
end;
function TCellHelper.GetVertAlignment: TsVertAlignment;
begin
Result := Worksheet.ReadVertAlignment(@self);
end;
function TCellHelper.GetWordwrap: Boolean;
begin
Result := Worksheet.ReadWordwrap(@self);
end;
function TCellHelper.GetWorkbook: TsWorkbook;
begin
Result := Worksheet.Workbook;
end;
procedure TCellHelper.SetBackgroundColor(AValue: TsColor);
begin
Worksheet.WriteBackgroundColor(@self, AValue);
end;
procedure TCellHelper.SetBorder(AValue: TsCellBorders);
begin
Worksheet.WriteBorders(@self, AValue);
end;
procedure TCellHelper.SetBorderStyle(ABorder: TsCellBorder;
AValue: TsCellBorderStyle);
begin
Worksheet.WriteBorderStyle(@self, ABorder, AValue);
end;
procedure TCellHelper.SetFontIndex(AValue: Integer);
begin
Worksheet.WriteFont(@self, AValue);
end;
end.

View File

@@ -1254,6 +1254,7 @@ var
stype: String;
r1, c1: Cardinal;
cell: PCell;
cellfmt: TsCellFormat;
begin
if Length(Args)=1 then
begin
@@ -1282,6 +1283,10 @@ begin
Result := ErrorResult(errWrongType);
exit;
end;
if cell <> nil then
cellfmt := cell^.Worksheet.ReadCellFormat(cell)
else
InitFormatRecord(cellfmt);
if stype = 'address' then
Result := StringResult(GetCellString(r1, c1, []))
@@ -1291,7 +1296,7 @@ begin
else
if stype = 'color' then
begin
if (cell <> nil) and (cell^.NumberFormat = nfCurrencyRed) then
if (cell <> nil) and (cellfmt.NumberFormat = nfCurrencyRed) then
Result := IntegerResult(1)
else
Result := IntegerResult(0);
@@ -1322,20 +1327,20 @@ begin
if stype = 'format' then begin
Result := StringResult('G');
if cell <> nil then
case cell^.NumberFormat of
case cellfmt.NumberFormat of
nfGeneral:
Result := StringResult('G');
nfFixed:
if cell^.NumberFormatStr= '0' then Result := StringResult('0') else
if cell^.NumberFormatStr = '0.00' then Result := StringResult('F0');
if cellfmt.NumberFormatStr= '0' then Result := StringResult('0') else
if cellfmt.NumberFormatStr = '0.00' then Result := StringResult('F0');
nfFixedTh:
if cell^.NumberFormatStr = '#,##0' then Result := StringResult(',0') else
if cell^.NumberFormatStr = '#,##0.00' then Result := StringResult(',2');
if cellfmt.NumberFormatStr = '#,##0' then Result := StringResult(',0') else
if cellfmt.NumberFormatStr = '#,##0.00' then Result := StringResult(',2');
nfPercentage:
if cell^.NumberFormatStr = '0%' then Result := StringResult('P0') else
if cell^.NumberFormatStr = '0.00%' then Result := StringResult('P2');
if cellfmt.NumberFormatStr = '0%' then Result := StringResult('P0') else
if cellfmt.NumberFormatStr = '0.00%' then Result := StringResult('P2');
nfExp:
if cell^.NumberFormatStr = '0.00E+00' then Result := StringResult('S2');
if cellfmt.NumberFormatStr = '0.00E+00' then Result := StringResult('S2');
nfShortDate, nfLongDate, nfShortDateTime:
Result := StringResult('D4');
nfLongTimeAM:
@@ -1352,7 +1357,7 @@ begin
begin
Result := StringResult('');
if (cell^.ContentType = cctUTF8String) then
case cell^.HorAlignment of
case cellfmt.HorAlignment of
haLeft : Result := StringResult('''');
haCenter: Result := StringResult('^');
haRight : Result := StringResult('"');

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -74,7 +74,7 @@ type
property YFirstCellRow: Cardinal read FYFirstCellRow write SetYFirstCellRow default 0;
property XSelectionDirection: TsSelectionDirection read FXSelectionDirection write SetXSelectionDirection;
property YSelectionDirection: TsSelectionDirection read FYSelectionDirection write SetYSelectionDirection;
end;
end deprecated 'Use TsWorkBOOKChartSource instead of TsWorkSHEETChartSource.';
{ TsWorkbookChartSource }

View File

@@ -2548,39 +2548,45 @@ var
s: String;
cb: TsCellBorder;
r1, r2, c1, c2: Cardinal;
fmt: TsCellFormat;
begin
if (ACell = nil) or not (uffFont in ACell^.UsedFormattingFields)
if (ACell <> nil) then
fmt := Workbook.GetCellFormat(ACell^.FormatIndex)
else
InitFormatRecord(fmt);
if (ACell = nil) or not (uffFont in fmt.UsedFormattingFields)
then AStrings.Add('FontIndex=')
else AStrings.Add(Format('FontIndex=%d (%s)', [
ACell^.FontIndex,
Workbook.GetFontAsString(ACell^.FontIndex)
fmt.FontIndex,
Workbook.GetFontAsString(fmt.FontIndex)
]));
if (ACell=nil) or not (uffTextRotation in ACell^.UsedFormattingFields)
if (ACell=nil) or not (uffTextRotation in fmt.UsedFormattingFields)
then AStrings.Add('TextRotation=')
else AStrings.Add(Format('TextRotation=%s', [
GetEnumName(TypeInfo(TsTextRotation), ord(ACell^.TextRotation))
GetEnumName(TypeInfo(TsTextRotation), ord(fmt.TextRotation))
]));
if (ACell=nil) or not (uffHorAlign in ACell^.UsedFormattingFields)
if (ACell=nil) or not (uffHorAlign in fmt.UsedFormattingFields)
then AStrings.Add('HorAlignment=')
else AStrings.Add(Format('HorAlignment=%s', [
GetEnumName(TypeInfo(TsHorAlignment), ord(ACell^.HorAlignment))
GetEnumName(TypeInfo(TsHorAlignment), ord(fmt.HorAlignment))
]));
if (ACell=nil) or not (uffVertAlign in ACell^.UsedFormattingFields)
if (ACell=nil) or not (uffVertAlign in fmt.UsedFormattingFields)
then AStrings.Add('VertAlignment=')
else AStrings.Add(Format('VertAlignment=%s', [
GetEnumName(TypeInfo(TsVertAlignment), ord(ACell^.VertAlignment))
GetEnumName(TypeInfo(TsVertAlignment), ord(fmt.VertAlignment))
]));
if (ACell=nil) or not (uffBorder in ACell^.UsedFormattingFields) then
if (ACell=nil) or not (uffBorder in fmt.UsedFormattingFields) then
AStrings.Add('Borders=')
else
begin
s := '';
for cb in TsCellBorder do
if cb in ACell^.Border then
if cb in fmt.Border then
s := s + ', ' + GetEnumName(TypeInfo(TsCellBorder), ord(cb));
if s <> '' then Delete(s, 1, 2);
AStrings.Add('Borders='+s);
@@ -2593,24 +2599,24 @@ begin
else
AStrings.Add(Format('BorderStyles[%s]=%s, %s', [
GetEnumName(TypeInfo(TsCellBorder), ord(cb)),
GetEnumName(TypeInfo(TsLineStyle), ord(ACell^.BorderStyles[cbEast].LineStyle)),
Workbook.GetColorName(ACell^.BorderStyles[cbEast].Color)]));
GetEnumName(TypeInfo(TsLineStyle), ord(fmt.BorderStyles[cbEast].LineStyle)),
Workbook.GetColorName(fmt.BorderStyles[cbEast].Color)]));
if (ACell = nil) or not (uffBackgroundColor in ACell^.UsedformattingFields)
if (ACell = nil) or not (uffBackgroundColor in fmt.UsedformattingFields)
then AStrings.Add('BackgroundColor=')
else AStrings.Add(Format('BackgroundColor=%d (%s)', [
ACell^.BackgroundColor,
Workbook.GetColorName(ACell^.BackgroundColor)]));
fmt.BackgroundColor,
Workbook.GetColorName(fmt.BackgroundColor)]));
if (ACell = nil) or not (uffNumberFormat in ACell^.UsedFormattingFields) then
if (ACell = nil) or not (uffNumberFormat in fmt.UsedFormattingFields) then
begin
AStrings.Add('NumberFormat=');
AStrings.Add('NumberFormatStr=');
end else
begin
AStrings.Add(Format('NumberFormat=%s', [
GetEnumName(TypeInfo(TsNumberFormat), ord(ACell^.NumberFormat))]));
AStrings.Add('NumberFormatStr=' + ACell^.NumberFormatStr);
GetEnumName(TypeInfo(TsNumberFormat), ord(fmt.NumberFormat))]));
AStrings.Add('NumberFormatStr=' + fmt.NumberFormatStr);
end;
if (Worksheet = nil) or not Worksheet.IsMerged(ACell) then

View File

@@ -849,6 +849,7 @@ var
txtalign: TsHorAlignment;
r: Cardinal;
w, w0: Integer;
fmt: PsCellFormat;
begin
Result := false;
cell := FDrawingCell;
@@ -856,16 +857,19 @@ begin
// Nothing to do in these cases (like in Excel):
if (cell = nil) or (cell^.ContentType <> cctUTF8String) then // ... non-label cells
exit;
if (uffWordWrap in cell^.UsedFormattingFields) then // ... word-wrap
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if (uffWordWrap in fmt^.UsedFormattingFields) then // ... word-wrap
exit;
if (uffTextRotation in cell^.UsedFormattingFields) and // ... vertical text
(cell^.TextRotation <> trHorizontal)
if (uffTextRotation in fmt^.UsedFormattingFields) and // ... vertical text
(fmt^.TextRotation <> trHorizontal)
then
exit;
txt := cell^.UTF8Stringvalue;
if (uffHorAlign in cell^.UsedFormattingFields) then
txtalign := cell^.HorAlignment
if (uffHorAlign in fmt^.UsedFormattingFields) then
txtalign := fmt^.HorAlignment
else
txtalign := haDefault;
PrepareCanvas(ACol, ARow, AState);
@@ -1160,6 +1164,7 @@ procedure TsCustomWorksheetGrid.DoPrepareCanvas(ACol, ARow: Integer;
var
ts: TTextStyle;
lCell: PCell;
fmt: PsCellFormat;
r, c: Integer;
fnt: TsFont;
style: TFontStyles;
@@ -1194,8 +1199,9 @@ begin
lCell := Worksheet.FindCell(r, c);
if lCell <> nil then
begin
fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex);
// Background color
if (uffBackgroundColor in lCell^.UsedFormattingFields) then
if (uffBackgroundColor in fmt^.UsedFormattingFields) then
begin
if Workbook.FileFormat = sfExcel2 then
begin
@@ -1206,8 +1212,8 @@ begin
end else
begin
Canvas.Brush.Style := bsSolid;
if lCell^.BackgroundColor < Workbook.GetPaletteSize then
Canvas.Brush.Color := Workbook.GetPaletteColor(lCell^.BackgroundColor)
if fmt^.BackgroundColor < Workbook.GetPaletteSize then
Canvas.Brush.Color := Workbook.GetPaletteColor(fmt^.BackgroundColor)
else
Canvas.Brush.Color := Color;
end;
@@ -1217,9 +1223,9 @@ begin
Canvas.Brush.Color := Color;
end;
// Font
if (uffFont in lCell^.UsedFormattingFields) then
if (uffFont in fmt^.UsedFormattingFields) then
begin
fnt := Workbook.GetFont(lCell^.FontIndex);
fnt := Workbook.GetFont(fmt^.FontIndex);
if fnt <> nil then
begin
Canvas.Font.Name := fnt.FontName;
@@ -1233,7 +1239,7 @@ begin
Canvas.Font.Size := round(fnt.Size);
end;
end;
if (lCell^.NumberFormat = nfCurrencyRed) and
if (fmt^.NumberFormat = nfCurrencyRed) and
not IsNaN(lCell^.NumberValue) and (lCell^.NumberValue < 0)
then
Canvas.Font.Color := Workbook.GetPaletteColor(scRed);
@@ -1302,7 +1308,7 @@ begin
cell := Worksheet.GetFirstCell;
while cell <> nil do
begin
if (uffBorder in cell^.UsedFormattingFields) then
if (uffBorder in Worksheet.ReadUsedFormatting(cell)) then
begin
c := GetGridCol(cell^.Col);
r := GetGridRow(cell^.Row);
@@ -1442,6 +1448,7 @@ const
var
bs: TsCellBorderStyle;
cell: PCell;
fmt: PsCellFormat;
begin
if Assigned(Worksheet) then begin
// Left border
@@ -1459,14 +1466,15 @@ begin
cell := Worksheet.FindCell(ARow-FHeaderCount, ACol-FHeaderCount);
if cell <> nil then begin
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
// Diagonal up
if cbDiagUp in cell^.Border then begin
bs := cell^.Borderstyles[cbDiagUp];
if cbDiagUp in fmt^.Border then begin
bs := fmt^.Borderstyles[cbDiagUp];
DrawBorderLine(0, ARect, drawDiagUp, bs);
end;
// Diagonal down
if cbDiagDown in cell^.Border then begin
bs := cell^.BorderStyles[cbDiagDown];
if cbDiagDown in fmt^.Border then begin
bs := fmt^.BorderStyles[cbDiagDown];
DrawborderLine(0, ARect, drawDiagDown, bs);
end;
end;
@@ -1527,6 +1535,7 @@ var
rct, saved_rct, temp_rct: TRect;
clipArea: Trect;
cell: PCell;
fmt: PsCellFormat;
tmp: Integer = 0;
function IsPushCellActive: boolean;
@@ -1606,9 +1615,10 @@ begin
then
Continue;
// Overflow possible from non-merged, non-right-aligned, horizontal label cells
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if (cell^.MergeBase = nil) and (cell^.ContentType = cctUTF8String) and
not (uffTextRotation in cell^.UsedFormattingFields) and
(uffHorAlign in cell^.UsedFormattingFields) and (cell^.HorAlignment <> haRight)
not (uffTextRotation in fmt^.UsedFormattingFields) and
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haRight)
then
Break;
// All other cases --> no overflow --> return to initial left cell
@@ -1631,9 +1641,10 @@ begin
then
continue;
// Overflow possible from non-merged, horizontal, non-left-aligned label cells
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if (cell^.MergeBase = nil) and (cell^.ContentType = cctUTF8String) and
not (uffTextRotation in cell^.UsedFormattingFields) and
(uffHorAlign in cell^.UsedFormattingFields) and (cell^.HorAlignment <> haLeft)
not (uffTextRotation in fmt^.UsedFormattingFields) and
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haLeft)
then
Break;
// All other cases --> no overflow --> return to initial right column
@@ -1786,6 +1797,7 @@ var
txtRot: TsTextRotation;
lCell: PCell;
justif: Byte;
fmt: PsCellFormat;
begin
if (Worksheet = nil) then
exit;
@@ -1810,12 +1822,13 @@ begin
end;
// Cells
wrapped := (uffWordWrap in lCell^.UsedFormattingFields) or (lCell^.TextRotation = rtStacked);
txtRot := lCell^.TextRotation;
vertAlign := lCell^.VertAlignment;
fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex);
wrapped := (uffWordWrap in fmt^.UsedFormattingFields) or (fmt^.TextRotation = rtStacked);
txtRot := fmt^.TextRotation;
vertAlign := fmt^.VertAlignment;
if vertAlign = vaDefault then vertAlign := vaBottom;
if lCell^.HorAlignment <> haDefault then
horAlign := lCell^.HorAlignment
if fmt^.HorAlignment <> haDefault then
horAlign := fmt^.HorAlignment
else
begin
if (lCell^.ContentType in [cctNumber, cctDateTime]) then
@@ -1915,7 +1928,7 @@ procedure TsCustomWorksheetGrid.FixNeighborCellBorders(ACol, ARow: Integer);
neighbor := Worksheet.FindCell(NewRow, NewCol);
if neighbor <> nil then
begin
border := neighbor^.Border;
border := Worksheet.ReadCelLBorders(neighbor);
if AInclude then
begin
Include(border, ANewBorder);
@@ -1928,6 +1941,7 @@ procedure TsCustomWorksheetGrid.FixNeighborCellBorders(ACol, ARow: Integer);
var
cell: PCell;
fmt: PsCellFormat;
begin
if Worksheet = nil then
exit;
@@ -1936,10 +1950,11 @@ begin
if (Worksheet <> nil) and (cell <> nil) then
with cell^ do
begin
SetNeighborBorder(Row, Col-1, cbEast, BorderStyles[cbWest], cbWest in Border);
SetNeighborBorder(Row, Col+1, cbWest, BorderStyles[cbEast], cbEast in Border);
SetNeighborBorder(Row-1, Col, cbSouth, BorderStyles[cbNorth], cbNorth in Border);
SetNeighborBorder(Row+1, Col, cbNorth, BorderStyles[cbSouth], cbSouth in Border);
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
SetNeighborBorder(Row, Col-1, cbEast, fmt^.BorderStyles[cbWest], cbWest in fmt^.Border);
SetNeighborBorder(Row, Col+1, cbWest, fmt^.BorderStyles[cbEast], cbEast in fmt^.Border);
SetNeighborBorder(Row-1, Col, cbSouth, fmt^.BorderStyles[cbNorth], cbNorth in fmt^.Border);
SetNeighborBorder(Row+1, Col, cbNorth, fmt^.BorderStyles[cbSouth], cbSouth in fmt^.Border);
end;
end;
@@ -2034,8 +2049,7 @@ begin
if Assigned(Worksheet) then
begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) and (uffBackgroundColor in cell^.UsedFormattingFields) then
Result := cell^.BackgroundColor;
Result := Worksheet.ReadBackgroundColor(cell);
end;
end;
@@ -2084,8 +2098,7 @@ begin
if Assigned(Worksheet) then
begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) and (uffBorder in cell^.UsedFormattingFields) then
Result := cell^.Border;
Result := Worksheet.ReadCellBorders(cell);
end;
end;
@@ -2137,8 +2150,7 @@ begin
if Assigned(Worksheet) then
begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then
Result := cell^.BorderStyles[ABorder];
Result := Worksheet.ReadCellBorderStyle(cell, ABorder);
end;
end;
@@ -2190,17 +2202,10 @@ begin
if (Workbook <> nil) then
begin
fnt := Workbook.GetDefaultFont;
if Worksheet <> nil then
if (Worksheet <> nil) then
begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then
begin
if (uffBold in cell^.UsedFormattingFields) then
fnt := Workbook.GetFont(1)
else
if (uffFont in cell^.UsedFormattingFields) then
fnt := Workbook.GetFont(cell^.FontIndex);
end;
fnt := Worksheet.ReadCellFont(cell);
end;
Convert_sFont_to_Font(fnt, FCellFont);
Result := FCellFont;
@@ -2253,7 +2258,7 @@ begin
cell := Worksheet.FindCell(GetWorksheetRow(r), GetWorksheetCol(c));
if cell <> nil then
begin
sFont := Workbook.GetFont(cell^.FontIndex);
sFont := Worksheet.ReadCellFont(cell);
if (sFont.FontName <> sDefFont.FontName) and (sFont.Size <> sDefFont.Size)
and (sFont.Style <> sDefFont.Style) and (sFont.Color <> sDefFont.Color)
then
@@ -2282,6 +2287,7 @@ var
cellR: TRect;
flags: Cardinal;
r1,c1,r2,c2: Cardinal;
fmt: PsCellFormat;
begin
Result := 0;
if ShowHeaders and ((ACol = 0) or (ARow = 0)) then
@@ -2292,7 +2298,6 @@ begin
lCell := Worksheet.FindCell(ARow-FHeaderCount, ACol-FHeaderCount);
if lCell <> nil then
begin
//if lCell^.MergedNeighbors <> [] then begin
if (lCell^.Mergebase <> nil) then
begin
Worksheet.FindMergedRange(lCell, r1, c1, r2, c2);
@@ -2306,15 +2311,16 @@ begin
if s = '' then
exit;
DoPrepareCanvas(ACol, ARow, []);
wrapped := (uffWordWrap in lCell^.UsedFormattingFields)
or (lCell^.TextRotation = rtStacked);
fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex);
wrapped := (uffWordWrap in fmt^.UsedFormattingFields)
or (fmt^.TextRotation = rtStacked);
// *** multi-line text ***
if wrapped then
begin
// horizontal
if ( (uffTextRotation in lCell^.UsedFormattingFields) and
(lCell^.TextRotation in [trHorizontal, rtStacked]))
or not (uffTextRotation in lCell^.UsedFormattingFields)
if ( (uffTextRotation in fmt^.UsedFormattingFields) and
(fmt^.TextRotation in [trHorizontal, rtStacked]))
or not (uffTextRotation in fmt^.UsedFormattingFields)
then
begin
cellR := CellRect(ACol, ARow);
@@ -2331,14 +2337,14 @@ begin
// *** single-line text ***
begin
// not rotated
if ( not (uffTextRotation in lCell^.UsedFormattingFields) or
(lCell^.TextRotation = trHorizontal) )
if ( not (uffTextRotation in fmt^.UsedFormattingFields) or
(fmt^.TextRotation = trHorizontal) )
then
Result := Canvas.TextHeight(s) + 2*constCellPadding
else
// rotated by +/- 90°
if (uffTextRotation in lCell^.UsedFormattingFields) and
(lCell^.TextRotation in [rt90DegreeClockwiseRotation, rt90DegreeCounterClockwiseRotation])
if (uffTextRotation in fmt^.UsedFormattingFields) and
(fmt^.TextRotation in [rt90DegreeClockwiseRotation, rt90DegreeCounterClockwiseRotation])
then
Result := Canvas.TextWidth(s) + 2*constCellPadding;
end;
@@ -2389,7 +2395,7 @@ begin
if lCell <> nil then
begin
Result := TrimToCell(lCell);
if lCell^.TextRotation = rtStacked then
if Worksheet.ReadTextRotation(lCell) = rtStacked then
begin
s := Result;
Result := '';
@@ -2495,7 +2501,7 @@ begin
then
result := false
else
ABorderStyle := cell^.BorderStyles[border]
ABorderStyle := Worksheet.ReadCellBorderStyle(cell, border)
end
else
// Only neighbor has border, cell has not
@@ -2506,7 +2512,7 @@ begin
then
result := false
else
ABorderStyle := neighborcell^.BorderStyles[neighborborder]
ABorderStyle := Worksheet.ReadCellBorderStyle(neighborcell, neighborborder); //neighborcell^.BorderStyles[neighborborder]
end
else
// Both cells have shared border -> use top or left border
@@ -2518,9 +2524,9 @@ begin
result := false
else
if (border in [cbNorth, cbWest]) then
ABorderStyle := neighborcell^.BorderStyles[neighborborder]
ABorderStyle := Worksheet.ReadCellBorderStyle(neighborcell, neighborborder) //neighborcell^.BorderStyles[neighborborder]
else
ABorderStyle := cell^.BorderStyles[border];
ABorderStyle := Worksheet.ReadCellBorderStyle(cell, border); //cell^.BorderStyles[border];
end else
Result := false;
end;
@@ -2608,8 +2614,10 @@ end;
-------------------------------------------------------------------------------}
function TsCustomWorksheetGrid.HasBorder(ACell: PCell; ABorder: TsCellBorder): Boolean;
begin
Result := (ACell <> nil) and (uffBorder in ACell^.UsedFormattingfields) and
(ABorder in ACell^.Border);
if Worksheet = nil then
result := false
else
Result := ABorder in Worksheet.ReadCellBorders(ACell);
end;
{@@ ----------------------------------------------------------------------------
@@ -3299,6 +3307,9 @@ end;
-------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.Setup;
begin
if csLoading in ComponentState then
exit;
if (Worksheet = nil) or (Worksheet.GetCellCount = 0) then begin
if ShowHeaders then begin
ColCount := FInitColCount + 1; //2;
@@ -3400,14 +3411,18 @@ var
p: Integer;
isRotated: Boolean;
isStacked: Boolean;
tr: TsTextRotation;
begin
Result := Worksheet.ReadAsUTF8Text(ACell);
if (Result = '') or ((ACell <> nil) and (ACell^.ContentType = cctUTF8String))
then
exit;
isRotated := (uffTextRotation in ACell^.UsedFormattingFields) and (ACell^.TextRotation <> trHorizontal);
isStacked := (uffTextRotation in ACell^.UsedFormattingFields) and (ACell^.TextRotation = rtStacked);
tr := Worksheet.ReadTextRotation(ACell);
isRotated := (tr <> trHorizontal);
isStacked := (tr = rtStacked);
// isRotated := (uffTextRotation in ACell^.UsedFormattingFields) and (ACell^.TextRotation <> trHorizontal);
// isStacked := (uffTextRotation in ACell^.UsedFormattingFields) and (ACell^.TextRotation = rtStacked);
// Determine space available in cell
if isRotated then
@@ -3548,11 +3563,8 @@ begin
Result := scNotDefined;
if (Workbook <> nil) and (Worksheet <> nil) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then begin
fnt := Workbook.GetFont(cell^.FontIndex);
if fnt <> nil then
Result := fnt.Color;
end;
fnt := Worksheet.ReadCellFont(cell);
Result := fnt.Color;
end;
end;
@@ -3581,11 +3593,8 @@ begin
Result := '';
if (Workbook <> nil) and (Worksheet <> nil) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then begin
fnt := Workbook.GetFont(cell^.FontIndex);
if fnt <> nil then
Result := fnt.FontName;
end;
fnt := Worksheet.ReadCellFont(cell);
Result := fnt.FontName;
end;
end;
@@ -3614,11 +3623,8 @@ begin
Result := -1.0;
if (Workbook <> nil) and (Worksheet <> nil) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then begin
fnt := Workbook.GetFont(cell^.FontIndex);
if fnt <> nil then
Result := fnt.Size;
end;
fnt := Worksheet.ReadCellFont(cell);
Result := fnt.Size;
end;
end;
@@ -3647,11 +3653,8 @@ begin
Result := [];
if (Workbook <> nil) and (Worksheet <> nil) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then begin
fnt := Workbook.GetFont(cell^.FontIndex);
if fnt <> nil then
Result := fnt.Style;
end;
fnt := Worksheet.ReadCellFont(cell);
Result := fnt.Style;
end;
end;
@@ -3679,8 +3682,7 @@ begin
Result := haDefault;
if Assigned(Worksheet) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if cell <> nil then
Result := cell^.HorAlignment;
Result := Worksheet.ReadHorAlignment(cell);
end;
end;
@@ -3718,8 +3720,7 @@ begin
Result := trHorizontal;
if Assigned(Worksheet) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) then
Result := cell^.TextRotation;
Result := Worksheet.ReadTextRotation(cell);
end;
end;
@@ -3747,8 +3748,7 @@ begin
Result := vaDefault;
if Assigned(Worksheet) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if cell <> nil then
Result := cell^.VertAlignment;
Result := Worksheet.ReadVertAlignment(cell);
end;
end;
@@ -3792,8 +3792,7 @@ begin
Result := false;
if Assigned(Worksheet) then begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (cell <> nil) and (uffWordwrap in cell^.UsedFormattingFields) then
Result := true;
Result := Worksheet.ReadWordwrap(cell);
end;
end;
@@ -3838,14 +3837,13 @@ end;
procedure TsCustomWorksheetGrid.SetBackgroundColor(ACol, ARow: Integer;
AValue: TsColor);
var
c, r: Cardinal;
cell: PCell;
begin
if Assigned(Worksheet) then begin
BeginUpdate;
try
c := GetWorksheetCol(ACol);
r := GetWorksheetRow(ARow);
Worksheet.WriteBackgroundColor(r, c, AValue);
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteBackgroundColor(cell, AValue);
finally
EndUpdate;
end;
@@ -3870,14 +3868,13 @@ end;
procedure TsCustomWorksheetGrid.SetCellBorder(ACol, ARow: Integer;
AValue: TsCellBorders);
var
c, r: Cardinal;
cell: PCell;
begin
if Assigned(Worksheet) then begin
BeginUpdate;
try
c := GetWorksheetCol(ACol);
r := GetWorksheetRow(ARow);
Worksheet.WriteBorders(r, c, AValue);
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteBorders(cell, AValue);
FixNeighborCellBorders(ACol, ARow);
finally
EndUpdate;
@@ -3902,11 +3899,14 @@ end;
procedure TsCustomWorksheetGrid.SetCellBorderStyle(ACol, ARow: Integer;
ABorder: TsCellBorder; AValue: TsCellBorderStyle);
var
cell: PCell;
begin
if Assigned(Worksheet) then begin
BeginUpdate;
try
Worksheet.WriteBorderStyle(GetWorksheetRow(ARow), GetWorksheetCol(ACol), ABorder, AValue);
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteBorderStyle(cell, ABorder, AValue);
FixNeighborCellBorders(ACol, ARow);
finally
EndUpdate;
@@ -3932,14 +3932,15 @@ end;
procedure TsCustomWorksheetGrid.SetCellFont(ACol, ARow: Integer; AValue: TFont);
var
fnt: TsFont;
cell: PCell;
begin
FCellFont.Assign(AValue);
if Assigned(Worksheet) then begin
fnt := TsFont.Create;
try
Convert_Font_To_sFont(FCellFont, fnt);
Worksheet.WriteFont(GetWorksheetRow(ARow), GetWorksheetCol(ACol),
fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteFont(cell, fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
finally
fnt.Free;
end;
@@ -3962,9 +3963,14 @@ begin
end;
procedure TsCustomWorksheetGrid.SetCellFontColor(ACol, ARow: Integer; AValue: TsColor);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteFontColor(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteFontColor(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetCellFontColors(ARect: TGridRect; AValue: TsColor);
@@ -3982,9 +3988,14 @@ begin
end;
procedure TsCustomWorksheetGrid.SetCellFontName(ACol, ARow: Integer; AValue: String);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteFontName(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteFontName(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetCellFontNames(ARect: TGridRect; AValue: String);
@@ -4003,9 +4014,14 @@ end;
procedure TsCustomWorksheetGrid.SetCellFontSize(ACol, ARow: Integer;
AValue: Single);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteFontSize(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteFontSize(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetCellFontSizes(ARect: TGridRect;
@@ -4025,9 +4041,14 @@ end;
procedure TsCustomWorksheetGrid.SetCellFontStyle(ACol, ARow: Integer;
AValue: TsFontStyles);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteFontStyle(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteFontStyle(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetCellFontStyles(ARect: TGridRect;
@@ -4073,9 +4094,14 @@ end;
procedure TsCustomWorksheetGrid.SetHorAlignment(ACol, ARow: Integer;
AValue: TsHorAlignment);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteHorAlignment(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteHorAlignment(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetHorAlignments(ARect: TGridRect;
@@ -4149,9 +4175,14 @@ end;
procedure TsCustomWorksheetGrid.SetTextRotation(ACol, ARow: Integer;
AValue: TsTextRotation);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteTextRotation(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteTextRotation(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetTextRotations(ARect: TGridRect;
@@ -4171,9 +4202,14 @@ end;
procedure TsCustomWorksheetGrid.SetVertAlignment(ACol, ARow: Integer;
AValue: TsVertAlignment);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteVertAlignment(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteVertAlignment(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetVertAlignments(ARect: TGridRect;
@@ -4193,9 +4229,14 @@ end;
procedure TsCustomWorksheetGrid.SetWordwrap(ACol, ARow: Integer;
AValue: Boolean);
var
cell: PCell;
begin
if Assigned(Worksheet) then
Worksheet.WriteWordwrap(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
begin
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteWordwrap(cell, AValue);
end;
end;
procedure TsCustomWorksheetGrid.SetWordwraps(ARect: TGridRect;

View File

@@ -16,7 +16,7 @@ unit fpsRPN;
interface
uses
SysUtils, fpstypes, fpspreadsheet;
SysUtils, fpstypes;
type
{@@ Pointer to a TPRNItem record

View File

@@ -37,9 +37,9 @@ const
MAX_COL_COUNT = 65535;
{@@ Name of the default font}
DEFAULTFONTNAME = 'Arial';
DEFAULT_FONTNAME = 'Arial';
{@@ Size of the default font}
DEFAULTFONTSIZE = 10;
DEFAULT_FONTSIZE = 10;
{@@ Takes account of effect of cell margins on row height by adding this
value to the nominal row height. Note that this is an empirical value
@@ -309,7 +309,7 @@ const
// not sure - but I think the mechanism with scRGBColor is not working...
// Will be removed sooner or later...
scRGBColor = $FFFF;
scRGBColor = $FFFD;
{@@ Identifier for transparent color }
scTransparent = $FFFE;
@@ -425,7 +425,9 @@ type
end;
{@@ Record containing all details for cell formatting }
TsStyle = record
TsCellFormat = record
Name: String;
ID: Integer;
UsedFormattingFields: TsUsedFormattingFields;
FontIndex: Integer;
TextRotation: TsTextRotation;
@@ -439,8 +441,211 @@ type
NumberFormatStr: String;
end;
{@@ Pointer to a format record }
PsCellFormat = ^TsCellFormat;
{@@ Specialized list for format records }
TsCellFormatList = class(TFPList)
private
FAllowDuplicates: Boolean;
function GetItem(AIndex: Integer): PsCellFormat;
procedure SetItem(AIndex: Integer; const AValue: PsCellFormat);
public
constructor Create(AAllowDuplicates: Boolean);
destructor Destroy; override;
function Add(const AItem: TsCellFormat): Integer; overload;
function Add(AItem: PsCellFormat): Integer; overload;
procedure Clear;
procedure Delete(AIndex: Integer);
function FindIndexOfID(ID: Integer): Integer;
function FindIndexOfName(AName: String): Integer;
function IndexOf(const AItem: TsCellFormat): Integer; overload;
property Items[AIndex: Integer]: PsCellFormat read GetItem write SetItem; default;
end;
procedure InitFormatRecord(out AValue: TsCellFormat);
implementation
{ Utilities }
procedure InitFormatRecord(out AValue: TsCellFormat);
begin
AValue.Name := '';
AValue.NumberFormatStr := '';
FillChar(AValue, SizeOf(AValue), 0);
AValue.BorderStyles := DEFAULT_BORDERSTYLES;
AValue.BackgroundColor := TsColor(-1);
end;
{ TsCellFormatList }
constructor TsCellFormatList.Create(AAllowDuplicates: Boolean);
begin
inherited Create;
FAllowDuplicates := AAllowDuplicates;
end;
destructor TsCellFormatList.Destroy;
begin
Clear;
inherited;
end;
function TsCellFormatList.Add(const AItem: TsCellFormat): Integer;
var
P: PsCellFormat;
begin
if FAllowDuplicates then
Result := -1
else
Result := IndexOf(AItem);
if Result = -1 then begin
New(P);
P^.Name := AItem.Name;
P^.ID := AItem.ID;
P^.UsedFormattingFields := AItem.UsedFormattingFields;
P^.FontIndex := AItem.FontIndex;
P^.TextRotation := AItem.TextRotation;
P^.HorAlignment := AItem.HorAlignment;
P^.VertAlignment := AItem.VertAlignment;
P^.Border := AItem.Border;
P^.BorderStyles := AItem.BorderStyles;
P^.BackgroundColor := AItem.BackgroundColor;
P^.RGBBackgroundColor := AItem.RGBBackgroundColor;
P^.NumberFormat := AItem.NumberFormat;
P^.NumberFormatStr := AItem.NumberFormatStr;
Result := inherited Add(P);
end;
end;
{@@ ----------------------------------------------------------------------------
Adds a pointer to a FormatRecord to the list. Allows nil for the predefined
formats which are not stored in the file.
-------------------------------------------------------------------------------}
function TsCellFormatList.Add(AItem: PsCellFormat): Integer;
begin
if AItem = nil then
Result := inherited Add(AItem)
else
Result := Add(AItem^);
end;
procedure TsCellFormatList.Clear;
var
i: Integer;
begin
for i:=Count-1 downto 0 do
Delete(i);
inherited;
end;
procedure TsCellFormatList.Delete(AIndex: Integer);
var
P: PsCellFormat;
begin
P := GetItem(AIndex);
if P <> nil then
Dispose(P);
inherited Delete(AIndex);
end;
function TsCellFormatList.GetItem(AIndex: Integer): PsCellFormat;
begin
Result := inherited Items[AIndex];
end;
function TsCellFormatList.FindIndexOfID(ID: Integer): Integer;
var
P: PsCellFormat;
begin
for Result := 0 to Count-1 do
begin
P := GetItem(Result);
if (P <> nil) and (P^.ID = ID) then
exit;
end;
Result := -1;
end;
function TsCellFormatList.FindIndexOfName(AName: String): Integer;
var
P: PsCellFormat;
begin
for Result := 0 to Count-1 do
begin
P := GetItem(Result);
if (P <> nil) and (P^.Name = AName) then
exit;
end;
Result := -1;
end;
function TsCellFormatList.IndexOf(const AItem: TsCellFormat): Integer;
var
P: PsCellFormat;
equ: Boolean;
b: TsCellBorder;
begin
for Result := 0 to Count-1 do
begin
P := GetItem(Result);
if (P = nil) then continue;
if (P^.UsedFormattingFields <> AItem.UsedFormattingFields) then continue;
if (uffFont in AItem.UsedFormattingFields) then
if (P^.FontIndex) <> (AItem.FontIndex) then continue;
if (uffTextRotation in AItem.UsedFormattingFields) then
if (P^.TextRotation <> AItem.TextRotation) then continue;
if (uffHorAlign in AItem.UsedFormattingFields) then
if (P^.HorAlignment <> AItem.HorAlignment) then continue;
if (uffVertAlign in AItem.UsedFormattingFields) then
if (P^.VertAlignment <> AItem.VertAlignment) then continue;
if (uffBorder in AItem.UsedFormattingFields) then begin
if (P^.Border <> AItem.Border) then continue;
equ := true;
for b in AItem.Border do begin
if (P^.BorderStyles[b].LineStyle <> AItem.BorderStyles[b].LineStyle) or
(P^.BorderStyles[b].Color <> Aitem.BorderStyles[b].Color)
then begin
equ := false;
break;
end;
end;
if not equ then continue;
end;
if (uffBackgroundColor in AItem.UsedFormattingFields) then begin
if (P^.BackgroundColor <> AItem.BackgroundColor) then continue;
if (AItem.BackgroundColor = scRGBColor) then
if (P^.RGBBackgroundColor <> AItem.RGBBackgroundColor) then continue;
end;
if (uffNumberFormat in AItem.UsedFormattingFields) then begin
if (P^.NumberFormat <> AItem.NumberFormat) then continue;
if (P^.NumberFormatStr <> AItem.NumberFormatStr) then continue;
end;
// If we arrive here then the format records match.
exit;
end;
// We get here if no record matches
Result := -1;
end;
procedure TsCellFormatList.SetItem(AIndex: Integer; const AValue: PsCellFormat);
begin
inherited Items[AIndex] := AValue;
end;
end.

View File

@@ -143,7 +143,6 @@ This package is all you need if you don't want graphical components (like grids
</Item28>
<Item29>
<Filename Value="fpstypes.pas"/>
<HasRegisterProc Value="True"/>
<UnitName Value="fpsTypes"/>
</Item29>
</Files>

View File

@@ -131,7 +131,6 @@
<Unit3>
<Filename Value="bebiffutils.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="beBIFFUtils"/>
</Unit3>
<Unit4>
<Filename Value="behtml.pas"/>
@@ -148,6 +147,7 @@
<Unit6>
<Filename Value="beutils.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="beUtils"/>
</Unit6>
<Unit7>
<Filename Value="mrumanager.pp"/>

View File

@@ -5,7 +5,7 @@ unit beBIFFGrid;
interface
uses
Classes, SysUtils, Controls, Grids, fpspreadsheet;
Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet;
type
TBIFFBuffer = array of byte;

View File

@@ -4,7 +4,7 @@ object MainForm: TMainForm
Top = 177
Width = 1089
Caption = 'BIFF Explorer'
ClientHeight = 551
ClientHeight = 556
ClientWidth = 1089
Menu = MainMenu
OnCloseQuery = FormCloseQuery
@@ -12,10 +12,10 @@ object MainForm: TMainForm
OnDestroy = FormDestroy
OnShow = FormShow
ShowHint = True
LCLVersion = '1.3'
LCLVersion = '1.5'
object Splitter1: TSplitter
Left = 419
Height = 496
Height = 506
Top = 27
Width = 5
end
@@ -49,6 +49,7 @@ object MainForm: TMainForm
end
object ToolButton4: TToolButton
Left = 59
Height = 25
Top = 2
Width = 5
Caption = 'ToolButton4'
@@ -57,17 +58,17 @@ object MainForm: TMainForm
end
object DetailPanel: TPanel
Left = 424
Height = 496
Height = 506
Top = 27
Width = 665
Align = alClient
BevelOuter = bvNone
ClientHeight = 496
ClientHeight = 506
ClientWidth = 665
TabOrder = 2
object PageControl: TPageControl
Left = 0
Height = 496
Height = 506
Top = 0
Width = 665
ActivePage = PgAnalysis
@@ -77,12 +78,12 @@ object MainForm: TMainForm
OnChange = PageControlChange
object PgAnalysis: TTabSheet
Caption = 'Analysis'
ClientHeight = 463
ClientHeight = 478
ClientWidth = 657
object AnalysisDetails: TMemo
Left = 0
Height = 191
Top = 272
Top = 287
Width = 657
Align = alBottom
Font.CharSet = ANSI_CHARSET
@@ -99,7 +100,7 @@ object MainForm: TMainForm
Cursor = crVSplit
Left = 0
Height = 5
Top = 267
Top = 282
Width = 657
Align = alBottom
ResizeAnchor = akBottom
@@ -193,7 +194,7 @@ object MainForm: TMainForm
20
20
20
20
24
)
Cells = (
16
@@ -263,22 +264,22 @@ object MainForm: TMainForm
OnClick = GridClick
OnSelection = AlphaGridSelection
ColWidths = (
15
15
15
15
15
15
15
15
15
15
15
15
15
15
15
27
16
16
16
16
16
16
16
16
16
16
16
16
16
16
16
16
)
Cells = (
16
@@ -355,19 +356,19 @@ object MainForm: TMainForm
end
object TreePanel: TPanel
Left = 0
Height = 496
Height = 506
Top = 27
Width = 419
Align = alLeft
BevelOuter = bvNone
ClientHeight = 496
ClientHeight = 506
ClientWidth = 419
Constraints.MinWidth = 275
TabOrder = 3
object FindPanel: TPanel
Left = 0
Height = 36
Top = 460
Top = 470
Width = 419
Align = alBottom
BevelOuter = bvNone
@@ -509,10 +510,10 @@ object MainForm: TMainForm
end
object CbFind: TComboBox
Left = 28
Height = 28
Height = 23
Top = 5
Width = 183
ItemHeight = 20
ItemHeight = 15
OnChange = CbFindChange
OnKeyPress = CbFindKeyPress
TabOrder = 0
@@ -520,7 +521,7 @@ object MainForm: TMainForm
end
object BIFFTree: TVirtualStringTree
Left = 0
Height = 460
Height = 470
Top = 0
Width = 419
Align = alClient
@@ -583,8 +584,8 @@ object MainForm: TMainForm
end
object StatusBar: TStatusBar
Left = 0
Height = 28
Top = 523
Height = 23
Top = 533
Width = 1089
Panels = <
item

View File

@@ -12,7 +12,7 @@ uses
{$else}
fpolestorage,
{$endif}
fpSpreadsheet,
fpstypes, fpSpreadsheet,
mrumanager, beBIFFGrid, types;
type
@@ -791,7 +791,18 @@ begin
exit;
end;
FFormat := sfExcel8;
// .xls files can contain several formats. We look into the header first.
if Lowercase(ExtractFileExt(AFileName))=STR_EXCEL_EXTENSION then
begin
valid := GetFormatFromFileHeader(AFileName, FFormat);
// It is possible that valid xls files are not detected correctly. Therefore,
// we open them explicitly by trial and error - see below.
if not valid then
FFormat := sfExcel8;
valid := true;
end else
FFormat := sfExcel8;
while True do begin
try
LoadFile(AFileName, FFormat);
@@ -1169,7 +1180,8 @@ begin
$0031, $0231: // Font record
begin
inc(FFontIndex);
data.Index := FFontIndex;
if FFontIndex > 3 then data.Index := FFontIndex + 1
else data.Index := FFontIndex;
end;
$0043, $00E0: // XF record
begin

View File

@@ -6,13 +6,16 @@ interface
uses
Classes, SysUtils, IniFiles, Forms,
fpspreadsheet;
fpstypes, fpspreadsheet;
function CreateIni : TCustomIniFile;
procedure ReadFormFromIni(ini: TCustomIniFile; ASection: String; AForm: TCustomForm);
procedure WriteFormToIni(ini: TCustomIniFile; ASection: String; AForm: TCustomForm);
function GetFileFormatName(AFormat: TsSpreadsheetFormat): String;
function GetFormatFromFileHeader(const AFileName: TFileName;
out SheetType: TsSpreadsheetFormat): Boolean;
implementation
@@ -83,5 +86,76 @@ begin
end;
end;
function GetFormatFromFileHeader(const AFileName: TFileName;
out SheetType: TsSpreadsheetFormat): Boolean;
const
BIFF2_HEADER: array[0..15] of byte = (
$09,$00, $04,$00, $00,$00, $10,$00, $31,$00, $0A,$00, $C8,$00, $00,$00);
BIFF58_HEADER: array[0..15] of byte = (
$D0,$CF, $11,$E0, $A1,$B1, $1A,$E1, $00,$00, $00,$00, $00,$00, $00,$00);
BIFF5_MARKER: array[0..7] of widechar = (
'B', 'o', 'o', 'k', #0, #0, #0, #0);
BIFF8_MARKER:array[0..7] of widechar = (
'W', 'o', 'r', 'k', 'b', 'o', 'o', 'k');
var
buf: packed array[0..16] of byte;
stream: TStream;
i: Integer;
ok: Boolean;
begin
buf[0] := 0; // Silence the compiler...
Result := false;
stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyNone);
try
// Read first 16 bytes
stream.ReadBuffer(buf, 16);
// Check for Excel 2#
ok := true;
for i:=0 to 15 do
if buf[i] <> BIFF2_HEADER[i] then
begin
ok := false;
break;
end;
if ok then
begin
SheetType := sfExcel2;
Exit(True);
end;
// Check for Excel 5 or 8
for i:=0 to 15 do
if buf[i] <> BIFF58_HEADER[i] then
exit;
// Further information begins at offset $480:
stream.Position := $480;
stream.ReadBuffer(buf, 16);
// Check for Excel5
ok := true;
for i:=0 to 7 do
if WideChar(buf[i*2]) <> BIFF5_MARKER[i] then
begin
ok := false;
break;
end;
if ok then
begin
SheetType := sfExcel5;
Exit(True);
end;
// Check for Excel8
for i:=0 to 7 do
if WideChar(buf[i*2]) <> BIFF8_MARKER[i] then
exit(false);
SheetType := sfExcel8;
Exit(True);
finally
stream.Free;
end;
end;
end.

View File

@@ -150,7 +150,7 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
currentRGB := MyWorkbook.GetPaletteColor(MyCell^.BackgroundColor);
currentRGB := MyWorkbook.GetPaletteColor(MyWorksheet.ReadBackgroundColor(MyCell));
expectedRGB := MyWorkbook.GetPaletteColor(color);
CheckEquals(expectedRGB, currentRGB,
'Test unsaved background color, cell ' + CellNotation(MyWorksheet,0,0));
@@ -176,7 +176,7 @@ begin
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
color := TsColor(row);
currentRGB := MyWorkbook.GetPaletteColor(MyCell^.BackgroundColor);
currentRGB := MyWorkbook.GetPaletteColor(MyWorksheet.ReadBackgroundColor(MyCell));
expectedRGB := pal[color];
CheckEquals(expectedRGB, currentRGB,
'Test saved background color, cell '+CellNotation(MyWorksheet,Row,Col));
@@ -239,7 +239,7 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
colorInFile := MyWorkbook.GetFont(MyCell^.FontIndex).Color;
colorInFile := MyWorksheet.ReadCellFont(MyCell).Color;
currentRGB := MyWorkbook.GetPaletteColor(colorInFile);
expectedRGB := MyWorkbook.GetPaletteColor(color);
CheckEquals(expectedRGB, currentRGB,
@@ -267,7 +267,7 @@ begin
fail('Error in test code. Failed to get cell.');
color := TsColor(row);
expectedRGB := pal[color];
colorInFile := MyWorkbook.GetFont(MyCell^.FontIndex).Color;
colorInFile := MyWorksheet.ReadCellFont(MyCell).Color;
currentRGB := MyWorkbook.GetPaletteColor(colorInFile);
// Excel2 cannot write the entire palette. The writer had called "FixColor".

View File

@@ -52,7 +52,7 @@ const
CopyTestSheet = 'Copy';
function InitNumber(ANumber: Double; ABkColor: TsColor): TCell;
begin
begin (*
InitCell(Result);
Result.ContentType := cctNumber;
Result.Numbervalue := ANumber;
@@ -60,11 +60,11 @@ begin
begin
Result.UsedFormattingFields := Result.UsedFormattingFields + [uffBackgroundColor];
Result.BackgroundColor := ABkColor;
end;
end; *)
end;
function InitString(AString: String; ABkColor: TsColor): TCell;
begin
begin (*
InitCell(Result);
Result.ContentType := cctUTF8String;
Result.UTF8StringValue := AString;
@@ -72,11 +72,11 @@ begin
begin
Result.UsedFormattingFields := Result.UsedFormattingFields + [uffBackgroundColor];
Result.BackgroundColor := ABkColor;
end;
end; *)
end;
function InitFormula(AFormula: String; ANumberResult: Double; ABkColor: TsColor): TCell;
begin
begin (*
InitCell(Result);
Result.FormulaValue := AFormula;
Result.NumberValue := ANumberResult;
@@ -85,7 +85,7 @@ begin
begin
Result.UsedFormattingFields := Result.UsedFormattingFields + [uffBackgroundColor];
Result.BackgroundColor := ABkColor;
end;
end; *)
end;
{ IMPORTANT: Carefully check the Test_Copy method if anything is changed here.

View File

@@ -137,6 +137,7 @@ var
row, col: Integer;
MyCell: PCell;
TempFile: string; //write xls/xml to this file and read back from it
fmt: TsCellFormat;
begin
{// Not needed: use workbook.writetofile with overwrite=true
if fileexists(TempFile) then
@@ -153,7 +154,8 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
CheckEquals(uffBold in MyCell^.UsedFormattingFields, false,
fmt := MyWorkbook.GetCellFormat(MyCell^.FormatIndex);
CheckEquals(false, uffBold in fmt.UsedFormattingFields,
'Test unsaved bold attribute, cell '+CellNotation(MyWorksheet,Row,Col));
// Write out a cell with "bold" formatting style
@@ -163,7 +165,8 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failded to get cell.');
CheckEquals(uffBold in MyCell^.UsedFormattingFields, true,
fmt := MyWorkbook.GetCellFormat(MyCell^.FormatIndex);
CheckEquals(true, uffBold in fmt.UsedFormattingFields,
'Test unsaved bold attribute, cell '+CellNotation(MyWorksheet,Row, Col));
TempFile:=NewTempFile;
@@ -189,7 +192,8 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
CheckEquals(uffBold in MyCell^.UsedFormattingFields, false,
fmt := MyWorkbook.GetCellFormat(MyCell^.FormatIndex);
CheckEquals(false, uffBold in fmt.UsedFormattingFields,
'Test saved bold attribute, cell '+CellNotation(MyWorksheet,row,col));
// Try to read cell with "bold"
@@ -197,7 +201,8 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
CheckEquals(uffBold in MyCell^.UsedFormattingFields, true,
fmt := MyWorkbook.GetCellFormat(MyCell^.FormatIndex);
CheckEquals(true, uffBold in fmt.UsedFormattingFields,
'Test saved bold attribute, cell '+CellNotation(MyWorksheet,row,col));
finally
MyWorkbook.Free;
@@ -240,7 +245,7 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
font := MyWorkbook.GetFont(MyCell^.FontIndex);
font := MyWorksheet.ReadCellFont(MyCell);
CheckEquals(SollSizes[row], font.Size,
'Test unsaved font size, cell ' + CellNotation(MyWorksheet,0,0));
currValue := GetEnumName(TypeInfo(TsFontStyles), integer(font.Style));
@@ -274,7 +279,7 @@ begin
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then
fail('Error in test code. Failed to get cell.');
font := MyWorkbook.GetFont(MyCell^.FontIndex);
font := MyWorksheet.ReadCellFont(MyCell);
if abs(SollSizes[row] - font.Size) > 1e-6 then // safe-guard against rounding errors
CheckEquals(SollSizes[row], font.Size,
'Test saved font size, cell '+CellNotation(MyWorksheet,Row,Col));

View File

@@ -1,6 +1,7 @@
unit formattests;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
interface
{ Formatted date/time/number tests
@@ -16,9 +17,8 @@ uses
{$ENDIF}
// Not using Lazarus package as the user may be working with multiple versions
// Instead, add .. to unit search path
Classes, SysUtils, fpcunit, testutils, testregistry,
fpstypes, fpsallformats, fpspreadsheet, xlsbiff8 {and a project requirement for lclbase for utf8 handling},
testsutility;
Classes, SysUtils, fpcunit, testutils, testregistry, testsutility,
fpstypes, fpsallformats, fpspreadsheet, fpshelpers, xlsbiff8;
var
// Norm to test against - list of strings that should occur in spreadsheet
@@ -708,6 +708,8 @@ var
Result := GetEnumName(TypeInfo(TsCellBorder), ord(cb))
else
Result := Result + ', ' + GetEnumName(TypeInfo(TsCellBorder), ord(cb));
if Result = '' then
Result := 'no borders';
end;
begin
@@ -725,10 +727,11 @@ begin
maxCol := High(SollBorders);
for col := Low(SollBorders) to maxCol do
begin
MyWorksheet.WriteUsedFormatting(row, col, [uffBorder]);
MyCell := MyWorksheet.GetCell(row, col);
Include(MyCell^.UsedFormattingFields, uffBorder);
MyCell^.Border := SollBorders[col];
// It is important for the test to write contents to the cell. Without it
// the first cell (col=0) would not even contain a format and would be
// dropped by the ods reader resulting in a matching error.
MyCell := MyWorksheet.WriteUTF8Text(row, col, GetBordersAsText(SollBorders[col]));
MyWorksheet.WriteBorders(MyCell, SollBorders[col]);
end;
TempFile:=NewTempFile;
@@ -809,6 +812,7 @@ var
TempFile: string; //write xls/xml to this file and read back from it
c, ls: Integer;
borders: TsCellBorders;
borderstyle: TsCellBorderStyle;
diagUp_ls: Integer;
diagUp_clr: integer;
begin
@@ -874,7 +878,8 @@ begin
fail('Error in test code. Failed to get cell.');
for b in borders do
begin
current := ord(MyCell^.BorderStyles[b].LineStyle);
borderStyle := MyWorksheet.ReadCellBorderStyle(MyCell, b);
current := ord(borderStyle.LineStyle);
// In Excel both diagonals have the same line style. The reader picks
// the line style of the "diagonal-up" border. We use this as expected
// value in the "diagonal-down" case.
@@ -886,7 +891,7 @@ begin
end;
CheckEquals(expected, current,
'Test saved border line style mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2));
current := MyCell^.BorderStyles[b].Color;
current := borderStyle.Color;
expected := SollBorderColors[c];
// In Excel both diagonals have the same line color. The reader picks
// the color of the "diagonal-up" border. We use this as expected value
@@ -1207,13 +1212,15 @@ begin
MyCell := MyWorksheet.FindCell(0, 0);
if MyCell = nil then
fail('Error in test code. Failed to get word-wrapped cell.');
CheckEquals(true, (uffWordWrap in MyCell^.UsedFormattingFields), 'Test unsaved word wrap mismatch cell ' + CellNotation(MyWorksheet,0,0));
CheckEquals(true, MyWorksheet.ReadWordwrap(MyCell),
'Test unsaved word wrap mismatch cell ' + CellNotation(MyWorksheet,0,0));
MyWorksheet.WriteUTF8Text(1, 0, LONGTEXT);
MyWorksheet.WriteUsedFormatting(1, 0, []);
MyCell := MyWorksheet.FindCell(1, 0);
if MyCell = nil then
fail('Error in test code. Failed to get word-wrapped cell.');
CheckEquals(false, (uffWordWrap in MyCell^.UsedFormattingFields), 'Test unsaved non-wrapped cell mismatch, cell ' + CellNotation(MyWorksheet,0,0));
CheckEquals(false, MyWorksheet.ReadWordwrap(MyCell),
'Test unsaved non-wrapped cell mismatch, cell ' + CellNotation(MyWorksheet,0,0));
TempFile:=NewTempFile;
MyWorkBook.WriteToFile(TempFile, AFormat, true);
finally
@@ -1233,12 +1240,12 @@ begin
MyCell := MyWorksheet.FindCell(0, 0);
if MyCell = nil then
fail('Error in test code. Failed to get word-wrapped cell.');
CheckEquals(true, (uffWordWrap in MyCell^.UsedFormattingFields),
CheckEquals(true, MyWorksheet.ReadWordwrap(MyCell),
'Failed to return correct word-wrap flag, cell ' + CellNotation(MyWorksheet,0,0));
MyCell := MyWorksheet.FindCell(1, 0);
if MyCell = nil then
fail('Error in test code. Failed to get non-wrapped cell.');
CheckEquals(false, (uffWordWrap in MyCell^.UsedFormattingFields),
CheckEquals(false, MyWorksheet.ReadWordwrap(MyCell),
'Failed to return correct word-wrap flag, cell ' + CellNotation(MyWorksheet,0,0));
finally
MyWorkbook.Free;

View File

@@ -21,7 +21,7 @@ uses
// Not using lazarus package as the user may be working with multiple versions
// Instead, add .. to unit search path
Classes, SysUtils, testutils, testregistry, testdecorator, fpcunit,
fpsallformats, fpspreadsheet,
fpsallformats, fpspreadsheet, fpshelpers,
xlsbiff8 {and a project requirement for lclbase for utf8 handling},
testsutility;
@@ -202,9 +202,7 @@ begin
for i:=0 to Workbook.GetPaletteSize-1 do begin
WorkSheet.WriteUTF8Text(i+RowOffset,0,'BACKGROUND COLOR TEST');
Cell := Worksheet.GetCell(i+RowOffset, 0);
Cell^.BackgroundColor := TsColor(i);
if not (uffBackgroundColor in Cell^.UsedFormattingFields) then
include (Cell^.UsedFormattingFields,uffBackgroundColor);
Worksheet.WriteBackgroundColor(Cell, TsColor(i));
WorkSheet.WriteUTF8Text(i+RowOffset,1,'Cell to the left should be '+Workbook.GetColorName(i)+'. Please check.');
end;
end;

View File

@@ -40,7 +40,7 @@
<PackageName Value="FCL"/>
</Item4>
</RequiredPackages>
<Units Count="21">
<Units Count="20">
<Unit0>
<Filename Value="spreadtestgui.lpr"/>
<IsPartOfProject Value="True"/>
@@ -48,22 +48,18 @@
<Unit1>
<Filename Value="datetests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="datetests"/>
</Unit1>
<Unit2>
<Filename Value="stringtests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="stringtests"/>
</Unit2>
<Unit3>
<Filename Value="numberstests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="numberstests"/>
</Unit3>
<Unit4>
<Filename Value="manualtests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="manualtests"/>
</Unit4>
<Unit5>
<Filename Value="testsutility.pas"/>
@@ -72,7 +68,6 @@
<Unit6>
<Filename Value="internaltests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="internaltests"/>
</Unit6>
<Unit7>
<Filename Value="formattests.pas"/>
@@ -82,12 +77,10 @@
<Unit8>
<Filename Value="colortests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="colortests"/>
</Unit8>
<Unit9>
<Filename Value="fonttests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="fonttests"/>
</Unit9>
<Unit10>
<Filename Value="optiontests.pas"/>
@@ -100,12 +93,10 @@
<Unit12>
<Filename Value="rpnformulaunit.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="rpnFormulaUnit"/>
</Unit12>
<Unit13>
<Filename Value="formulatests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="formulatests"/>
</Unit13>
<Unit14>
<Filename Value="emptycelltests.pas"/>
@@ -131,13 +122,7 @@
<Unit19>
<Filename Value="sortingtests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="sortingtests"/>
</Unit19>
<Unit20>
<Filename Value="copytests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="copytests"/>
</Unit20>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@@ -12,7 +12,7 @@ uses
manualtests, testsutility, internaltests, formattests, colortests, fonttests,
optiontests, numformatparsertests, formulatests, rpnFormulaUnit,
emptycelltests, errortests, virtualmodetests, insertdeletetests,
celltypetests, sortingtests, copytests;
celltypetests, sortingtests;
begin
{$IFDEF HEAPTRC}

View File

@@ -375,9 +375,11 @@ procedure TsWikiTableWriter.WriteToStrings_WikiMedia(AStrings: TStrings);
var
ls: TsLineStyle;
clr: TsColor;
fmt: PsCellFormat;
begin
ls := ACell^.BorderStyles[ABorder].LineStyle;
clr := ACell^.BorderStyles[ABorder].Color;
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
ls := fmt^.BorderStyles[ABorder].LineStyle;
clr := fmt^.BorderStyles[ABorder].Color;
Result := Format('border-%s:%s', [BORDERNAMES[ABorder], LINESTYLES[ls]]);
if clr <> scBlack then
Result := Result + ' ' + FWorkbook.GetPaletteColorAsHTMLStr(clr) + '; ';
@@ -403,6 +405,7 @@ var
vertalign: TsVertAlignment;
r1, c1, r2, c2: Cardinal;
isHeader: Boolean;
borders: TsCellBorders;
begin
FWorksheet := Workbook.GetFirstWorksheet();
FWorksheet.UpdateCaches;
@@ -418,7 +421,7 @@ begin
// Default font
lStyleStr := '';
lFont := FWorkbook.GetDefaultFont;
if lFont.FontName <> DEFAULTFONTNAME then
if lFont.FontName <> DEFAULT_FONTNAME then
lStyleStr := lStyleStr + Format('font-family:%s;', [lFont.FontName]);
if fssBold in lFont.Style then
lStyleStr := lStyleStr + 'font-weight:bold;';
@@ -426,7 +429,7 @@ begin
lStyleStr := lStyleStr + 'font-style:italic;';
if fssUnderline in lFont.Style then
lStyleStr := lStyleStr + 'text-decoration:underline;';
if lFont.Size <> DEFAULTFONTSIZE then
if lFont.Size <> DEFAULT_FONTSIZE then
lStyleStr := lStyleStr + Format('font-size:%.0fpt;', [lFont.Size]);
if lStyleStr <> '' then
lCurStr := lCurStr + ' style="' + lStyleStr + '"';
@@ -480,7 +483,7 @@ begin
lFont := FWorkbook.GetDefaultFont;
if (uffFont in lCurUsedFormatting) then
begin
lFont := FWorkbook.GetFont(lCell^.FontIndex);
lFont := FWorksheet.ReadCellFont(lCell);
if fssBold in lFont.Style then lCurStr := '<b>' + lCurStr + '</b>';
if fssItalic in lFont.Style then lCurStr := '<i>' + lCurStr + '</i>';
if fssUnderline in lFont.Style then lCurStr := '<u>' + lCurStr + '</u>';
@@ -502,7 +505,7 @@ begin
// Horizontal alignment
if uffHorAlign in lCurUsedFormatting then
begin
horAlign := lCell^.HorAlignment;
horAlign := FWorksheet.ReadHorAlignment(lCell);
if horAlign = haDefault then
case lCell^.ContentType of
cctNumber,
@@ -520,7 +523,7 @@ begin
// vertical alignment
if uffVertAlign in lCurUsedFormatting then
begin
vertAlign := lCell^.VertAlignment;
vertAlign := FWorksheet.ReadVertAlignment(lCell);
case vertAlign of
vaTop : lStyleStr := lStyleStr + 'vertical-align:top;';
vaCenter : lStyleStr := lStyleStr + 'vertical-align:center;';
@@ -531,13 +534,14 @@ begin
// borders
if uffBorder in lCurUsedFormatting then
begin
if (cbWest in lCell^.Border) then
borders := FWorksheet.ReadCellBorders(lCell);
if (cbWest in borders) then
lStyleStr := lStyleStr + DoBorder(cbWest, lCell);
if (cbEast in lCell^.Border) then
if (cbEast in borders) then
lStyleStr := lStyleStr + DoBorder(cbEast, lCell);
if (cbNorth in lCell^.Border) then
if (cbNorth in borders) then
lStyleStr := lStyleStr + DoBorder(cbNorth, lCell);
if (cbSouth in lCell^.Border) then
if (cbSouth in borders) then
lStyleStr := lStyleStr + DoBorder(cbSouth, lCell);
end;

View File

@@ -50,7 +50,7 @@ type
constructor Create(AWorkbook: TsWorkbook);
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat); override;
function FindFormatOf(AFormatCell: PCell): Integer; override;
function Find(ANumFormat: TsNumberFormat; ANumFormatStr: String): Integer; override; overload;
end;
{ TsSpreadBIFF2Reader }
@@ -60,10 +60,12 @@ type
WorkBookEncoding: TsEncoding;
FFont: TsFont;
protected
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); override;
// procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); override;
procedure CreateNumFormatList; override;
{
procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); override;
}
procedure ReadBlank(AStream: TStream); override;
procedure ReadBool(AStream: TStream); override;
procedure ReadColWidth(AStream: TStream);
@@ -93,7 +95,6 @@ type
TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter)
private
function FindXFIndex(ACell: PCell): Word;
procedure GetCellAttributes(ACell: PCell; XFIndex: Word;
out Attrib1, Attrib2, Attrib3: Byte);
{ Record writing methods }
@@ -107,11 +108,11 @@ type
procedure WriteFonts(AStream: TStream);
procedure WriteFormatCount(AStream: TStream);
procedure WriteIXFE(AStream: TStream; XFIndex: Word);
{
procedure WriteXF(AStream: TStream; AFontIndex, AFormatIndex: byte;
ABorders: TsCellBorders = []; AHorAlign: TsHorAlignment = haLeft;
AddBackground: Boolean = false);
procedure WriteXFFieldsForFormattingStyles(AStream: TStream);
procedure WriteXFRecords(AStream: TStream);
AddBackground: Boolean = false); }
// procedure WriteXFFieldsForFormattingStyles(AStream: TStream);
protected
procedure CreateNumFormatList; override;
procedure ListAllNumFormats; override;
@@ -121,12 +122,12 @@ type
const AValue: Boolean; ACell: PCell); override;
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TsErrorValue; ACell: PCell); override;
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); 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 WriteNumFormat(AStream: TStream; ANumFormatData: TsNumFormatData;
AListIndex: Integer); override;
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); override;
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
@@ -139,6 +140,8 @@ type
procedure WriteStringRecord(AStream: TStream; AString: String); override;
procedure WriteWindow1(AStream: TStream); override;
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
procedure WriteXF(AStream: TStream; AFormatRecord: PsCellFormat;
XFType_Prot: Byte = 0); override;
public
constructor Create(AWorkbook: TsWorkbook); override;
{ General writing methods }
@@ -158,8 +161,6 @@ var
$00FFFF // $07: cyan
);
// However, it looks as if BIFF2 can handle more colors, at least 16 are
// compatible with the other formats.
implementation
@@ -192,7 +193,7 @@ const
{%H-}INT_EXCEL_MACRO_SHEET = $0040;
type
TBIFF2BoolErrRecord = packed record
TBIFF2_BoolErrRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -204,7 +205,7 @@ type
ValueType: Byte;
end;
TBIFF2DimensionsRecord = packed record
TBIFF2_DimensionsRecord = packed record
RecordID: Word;
RecordSize: Word;
FirstRow: Word;
@@ -213,7 +214,7 @@ type
LastColPlus1: Word;
end;
TBIFF2LabelRecord = packed record
TBIFF2_LabelRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -224,7 +225,7 @@ type
TextLen: Byte;
end;
TBIFF2NumberRecord = packed record
TBIFF2_NumberRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -235,7 +236,7 @@ type
Value: Double;
end;
TBIFF2IntegerRecord = packed record
TBIFF2_IntegerRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -246,6 +247,15 @@ type
Value: Word;
end;
TBIFF2_XFRecord = packed record
RecordID: Word;
RecordSize: Word;
FontIndex: Byte;
NotUsed: Byte;
NumFormatIndex_Flags: Byte;
HorAlign_Border_BkGr: Byte;
end;
{ TsBIFF2NumFormatList }
@@ -265,30 +275,30 @@ var
begin
fs := FWorkbook.FormatSettings;
cs := fs.CurrencyString;
AddFormat( 0, '', nfGeneral);
AddFormat( 1, '0', nfFixed);
AddFormat( 2, '0.00', nfFixed);
AddFormat( 3, '#,##0', nfFixedTh);
AddFormat( 4, '#,##0.00', nfFixedTh);
AddFormat( 5, '"'+cs+'"#,##0;("'+cs+'"#,##0)', nfCurrency);
AddFormat( 6, '"'+cs+'"#,##0;[Red]("'+cs+'"#,##0)', nfCurrencyRed);
AddFormat( 7, '"'+cs+'"#,##0.00;("'+cs+'"#,##0.00)', nfCurrency);
AddFormat( 8, '"'+cs+'"#,##0.00;[Red]("'+cs+'"#,##0.00)', nfCurrency);
AddFormat( 9, '0%', nfPercentage);
AddFormat(10, '0.00%', nfPercentage);
AddFormat(11, '0.00E+00', nfExp);
AddFormat(12, fs.ShortDateFormat, nfShortDate);
AddFormat(13, fs.LongDateFormat, nfLongDate);
AddFormat(14, 'd/mmm', nfCustom);
AddFormat(15, 'mmm/yy', nfCustom);
AddFormat(16, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM);
AddFormat(17, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM);
AddFormat(18, fs.ShortTimeFormat, nfShortTime);
AddFormat(19, fs.LongTimeFormat, nfLongTime);
AddFormat(20, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime);
AddFormat( 0, nfGeneral, '');
AddFormat( 1, nfFixed, '0');
AddFormat( 2, nfFixed, '0.00');
AddFormat( 3, nfFixedTh, '#,##0');
AddFormat( 4, nfFixedTh, '#,##0.00');
AddFormat( 5, nfCurrency, Format('"%s"#,##0;("%s"#,##0)', [cs, cs]));
AddFormat( 6, nfCurrencyRed, Format('"%s"#,##0;[Red]("%s"#,##0)', [cs, cs]));
AddFormat( 7, nfCurrency, Format('"%s"#,##0.00;("%s"#,##0.00)', [cs, cs]));
AddFormat( 8, nfCurrencyRed, Format('"%s"#,##0.00;[Red]("%s"#,##0.00)', [cs, cs]));
AddFormat( 9, nfPercentage, '0%');
AddFormat(10, nfPercentage, '0.00%');
AddFormat(11, nfExp, '0.00E+00');
AddFormat(12, nfShortDate, fs.ShortDateFormat);
AddFormat(13, nfLongDate, fs.LongDateFormat);
AddFormat(14, nfCustom, 'd/mmm');
AddFormat(15, nfCustom, 'mmm/yy');
AddFormat(16, nfShortTimeAM, AddAMPM(fs.ShortTimeFormat, fs));
AddFormat(17, nfLongTimeAM, AddAMPM(fs.LongTimeFormat, fs));
AddFormat(18, nfShortTime, fs.ShortTimeFormat);
AddFormat(19, nfLongTime, fs.LongTimeFormat);
AddFormat(20, nfShortDateTime, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat);
FFirstFormatIndexInFile := 0; // BIFF2 stores built-in formats to file.
FNextFormatIndex := 21; // not needed - there are not user-defined formats
FFirstNumFormatIndexInFile := 0; // BIFF2 stores built-in formats to file.
FNextNumFormatIndex := 21; // not needed - there are not user-defined formats
end;
@@ -313,7 +323,44 @@ begin
end;
end;
function TsBIFF2NumFormatList.Find(ANumFormat: TsNumberFormat;
ANumFormatStr: String): Integer;
var
parser: TsNumFormatParser;
decs: Integer;
dt: string;
begin
Result := 0;
parser := TsNumFormatParser.Create(Workbook, ANumFormatStr);
try
decs := parser.Decimals;
dt := parser.GetDateTimeCode(0);
finally
parser.Free;
end;
case ANumFormat of
nfGeneral : exit;
nfFixed : Result := IfThen(decs = 0, 1, 2);
nfFixedTh : Result := IfThen(decs = 0, 3, 4);
nfCurrency : Result := IfThen(decs = 0, 5, 7);
nfCurrencyRed : Result := IfThen(decs = 0, 6, 8);
nfPercentage : Result := IfThen(decs = 0, 9, 10);
nfExp : Result := 11;
nfShortDate : Result := 12;
nfLongDate : Result := 13;
nfShortTimeAM : Result := 16;
nfLongTimeAM : Result := 17;
nfShortTime : Result := 18;
nfLongTime : Result := 19;
nfShortDateTime: Result := 20;
nfCustom : if dt = 'dm' then Result := 14 else
if dt = 'my' then Result := 15;
end;
end;
(*
function TsBIFF2NumFormatList.FindFormatOf(AFormatCell: PCell): Integer;
var
parser: TsNumFormatParser;
@@ -349,7 +396,7 @@ begin
if dt = 'my' then Result := 15;
end;
end;
*)
{ TsSpreadBIFF2Reader }
@@ -358,7 +405,7 @@ begin
inherited Create(AWorkbook);
FLimitations.MaxPaletteSize := BIFF2_MAX_PALETTE_SIZE;
end;
(*
procedure TsSpreadBIFF2Reader.ApplyCellFormatting(ACell: PCell; XFIndex: Word);
var
xfData: TXFListData;
@@ -396,7 +443,7 @@ begin
Exclude(ACell^.UsedFormattingFields, uffBackgroundColor);
end;
end;
*)
{ Creates the correct version of the number format list.
It is for BIFF2 and BIFF3 file formats. }
procedure TsSpreadBIFF2Reader.CreateNumFormatList;
@@ -404,7 +451,7 @@ begin
FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFF2NumFormatList.Create(Workbook);
end;
(*
{ Extracts the number format data from an XF record indexed by AXFIndex.
Note that BIFF2 supports only 21 formats. }
procedure TsSpreadBIFF2Reader.ExtractNumberFormat(AXFIndex: WORD;
@@ -421,7 +468,7 @@ begin
ANumberFormatStr := '';
end;
end;
*)
procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream);
var
ARow, ACol: Cardinal;
@@ -443,14 +490,14 @@ end;
but also an ERROR value; BIFF stores them in the same record. }
procedure TsSpreadBIFF2Reader.ReadBool(AStream: TStream);
var
rec: TBIFF2BoolErrRecord;
rec: TBIFF2_BoolErrRecord;
r, c: Cardinal;
xf: Word;
cell: PCell;
begin
{ Read entire record, starting at Row }
rec.Row := 0; // to silence the compiler...
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2BoolErrRecord) - 2*SizeOf(Word));
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2_BoolErrRecord) - 2*SizeOf(Word));
r := WordLEToN(rec.Row);
c := WordLEToN(rec.Col);
xf := rec.Attrib1 and $3F;
@@ -691,7 +738,7 @@ end;
procedure TsSpreadBIFF2Reader.ReadLabel(AStream: TStream);
var
rec: TBIFF2LabelRecord;
rec: TBIFF2_LabelRecord;
L: Byte;
ARow, ACol: Cardinal;
XF: Word;
@@ -701,7 +748,7 @@ var
begin
{ Read entire record, starting at Row, except for string data }
rec.Row := 0; // to silence the compiler...
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2LabelRecord) - 2*SizeOf(Word));
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2_LabelRecord) - 2*SizeOf(Word));
ARow := WordLEToN(rec.Row);
ACol := WordLEToN(rec.Col);
XF := rec.Attrib1 and $3F;
@@ -741,7 +788,7 @@ end;
procedure TsSpreadBIFF2Reader.ReadNumber(AStream: TStream);
var
rec: TBIFF2NumberRecord;
rec: TBIFF2_NumberRecord;
ARow, ACol: Cardinal;
XF: Word;
value: Double = 0.0;
@@ -752,7 +799,7 @@ var
begin
{ Read entire record, starting at Row }
rec.Row := 0; // to silence the compiler...
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2NumberRecord) - 2*SizeOf(Word));
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2_NumberRecord) - 2*SizeOf(Word));
ARow := WordLEToN(rec.Row);
ACol := WordLEToN(rec.Col);
XF := rec.Attrib1 and $3F;
@@ -785,11 +832,11 @@ var
XF: Word;
AWord : Word = 0;
cell: PCell;
rec: TBIFF2IntegerRecord;
rec: TBIFF2_IntegerRecord;
begin
{ Read record into buffer }
rec.Row := 0; // to silence the comiler...
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2NumberRecord) - 2*SizeOf(Word));
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF2_NumberRecord) - 2*SizeOf(Word));
ARow := WordLEToN(rec.Row);
ACol := WordLEToN(rec.Col);
XF := rec.Attrib1 and $3F;
@@ -969,7 +1016,8 @@ procedure TsSpreadBIFF2Reader.ReadXF(AStream: TStream);
4 10H 1 = Cell has right black border
5 20H 1 = Cell has top black border
6 40H 1 = Cell has bottom black border
7 80H 1 = Cell has shaded background }
7 80H 1 = Cell has shaded background
type
TXFRecord = packed record
FontIndex: byte;
@@ -977,60 +1025,80 @@ type
NumFormat_Flags: byte;
HorAlign_Border_BackGround: Byte;
end;
}
var
lData: TXFListData;
xf: TXFRecord;
rec: TBIFF2_XFRecord;
fmt: TsCellFormat;
b: Byte;
nfdata: TsNumFormatData;
i: Integer;
begin
// Read entire xf record into buffer
xf.FontIndex := 0; // to silence the compiler...
AStream.ReadBuffer(xf, SizeOf(xf));
InitFormatRecord(fmt);
fmt.ID := FCellFormatList.Count;
lData := TXFListData.Create;
rec.FontIndex := 0; // to silence the compiler...
AStream.ReadBuffer(rec.FontIndex, SizeOf(rec) - 2*SizeOf(word));
// Font index
lData.FontIndex := xf.FontIndex;
fmt.FontIndex := rec.FontIndex;
if fmt.FontIndex = 1 then
Include(fmt.UsedFormattingFields, uffBold)
else if fmt.FontIndex > 1 then
Include(fmt.UsedFormattingFields, uffFont);
// Format index
lData.FormatIndex := xf.NumFormat_Flags and $3F;
// Number format index
b := rec.NumFormatIndex_Flags and $3F;
i := NumFormatList.FindByIndex(b);
if i > -1 then begin
nfdata := NumFormatList.Items[i];
fmt.NumberFormat := nfdata.NumFormat;
fmt.NumberFormatStr := nfdata.FormatString;
if nfdata.NumFormat <> nfGeneral then
Include(fmt.UsedFormattingFields, uffNumberFormat);
end;
// Horizontal alignment
b := xf.HorAlign_Border_Background and MASK_XF_HOR_ALIGN;
b := rec.HorAlign_Border_BkGr and MASK_XF_HOR_ALIGN;
if (b <= ord(High(TsHorAlignment))) then
lData.HorAlignment := TsHorAlignment(b)
else
lData.HorAlignment := haDefault;
begin
fmt.HorAlignment := TsHorAlignment(b);
if fmt.HorAlignment <> haDefault then
Include(fmt.UsedFormattingFields, uffHorAlign);
end;
// Vertical alignment - not used in BIFF2
lData.VertAlignment := vaBottom;
fmt.VertAlignment := vaDefault;
// Word wrap - not used in BIFF2
lData.WordWrap := false;
// -- nothing to do here
// Text rotation - not used in BIFF2
lData.TextRotation := trHorizontal;
// -- nothing to do here
// Borders
lData.Borders := [];
if xf.HorAlign_Border_Background and $08 <> 0 then
Include(lData.Borders, cbWest);
if xf.HorAlign_Border_Background and $10 <> 0 then
Include(lData.Borders, cbEast);
if xf.HorAlign_Border_Background and $20 <> 0 then
Include(lData.Borders, cbNorth);
if xf.HorAlign_Border_Background and $40 <> 0 then
Include(lData.Borders, cbSouth);
fmt.Border := [];
if rec.HorAlign_Border_BkGr and $08 <> 0 then
Include(fmt.Border, cbWest);
if rec.HorAlign_Border_BkGr and $10 <> 0 then
Include(fmt.Border, cbEast);
if rec.HorAlign_Border_BkGr and $20 <> 0 then
Include(fmt.Border, cbNorth);
if rec.HorAlign_Border_BkGr and $40 <> 0 then
Include(fmt.Border, cbSouth);
if fmt.Border <> [] then
Include(fmt.UsedFormattingFields, uffBorder);
// Background color not supported, only shaded background
if xf.HorAlign_Border_Background and $80 <> 0 then
lData.BackgroundColor := 1 // encodes "shaded background = true"
else
ldata.BackgroundColor := 0; // encodes "shaded background = false"
if rec.HorAlign_Border_BkGr and $80 <> 0 then
begin
fmt.BackgroundColor := 1; // encodes "shaded background = true"
Include(fmt.UsedFormattingFields, uffBackgroundColor);
end else
fmt.BackgroundColor := 0; // encodes "shaded background = false"
// Add the decoded data to the list
FXFList.Add(lData);
ldata := TXFListData(FXFList.Items[FXFList.Count-1]);
// Add the decoded data to the format list
FCellFormatList.Add(fmt);
end;
@@ -1049,7 +1117,7 @@ begin
FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFF2NumFormatList.Create(Workbook);
end;
(*
function TsSpreadBIFF2Writer.FindXFIndex(ACell: PCell): Word;
var
lIndex: Integer;
@@ -1065,18 +1133,22 @@ begin
// Carefully check the index
if (lIndex < 0) or (lIndex > Length(FFormattingStyles)) then
raise Exception.Create('[TsSpreadBIFF2Writer.WriteXFIndex] Invalid index, this should not happen!');
raise Exception.Create('[TsSpreadBIFF2Writer.FindXFIndex] Invalid index, this should not happen!');
Result := FFormattingStyles[lIndex].Row;
end;
end;
end; *)
{ Determines the cell attributes needed for writing a cell content record, such
as WriteLabel, WriteNumber, etc.
The cell attributes contain, in bit masks, xf record index, font index, borders, etc.}
procedure TsSpreadBIFF2Writer.GetCellAttributes(ACell: PCell; XFIndex: Word;
out Attrib1, Attrib2, Attrib3: Byte);
var
fmt: PsCellFormat;
begin
if ACell^.UsedFormattingFields = [] then begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
if fmt^.UsedFormattingFields = [] then begin
Attrib1 := 15;
Attrib2 := 0;
Attrib3 := 0;
@@ -1090,9 +1162,9 @@ begin
Attrib1 := Min(XFIndex, $3F) and $3F;
// 2nd byte:
// Mask $3F: Index to FORMAT record
// Mask $3F: Index to FORMAT record ("FORMAT" = number format!)
// Mask $C0: Index to FONT record
Attrib2 := ACell^.FontIndex shr 6;
Attrib2 := fmt^.FontIndex shr 6;
// 3rd byte
// Mask $07: horizontal alignment
@@ -1102,15 +1174,15 @@ begin
// Mask $40: Cell has bottom border
// Mask $80: Cell has shaded background
Attrib3 := 0;
if uffHorAlign in ACell^.UsedFormattingFields then
Attrib3 := ord (ACell^.HorAlignment);
if uffBorder in ACell^.UsedFormattingFields then begin
if cbNorth in ACell^.Border then Attrib3 := Attrib3 or $20;
if cbWest in ACell^.Border then Attrib3 := Attrib3 or $08;
if cbEast in ACell^.Border then Attrib3 := Attrib3 or $10;
if cbSouth in ACell^.Border then Attrib3 := Attrib3 or $40;
if uffHorAlign in fmt^.UsedFormattingFields then
Attrib3 := ord (fmt^.HorAlignment);
if uffBorder in fmt^.UsedFormattingFields then begin
if cbNorth in fmt^.Border then Attrib3 := Attrib3 or $20;
if cbWest in fmt^.Border then Attrib3 := Attrib3 or $08;
if cbEast in fmt^.Border then Attrib3 := Attrib3 or $10;
if cbSouth in fmt^.Border then Attrib3 := Attrib3 or $40;
end;
if uffBackgroundColor in ACell^.UsedFormattingFields then
if (uffBackgroundColor in fmt^.UsedFormattingFields) and (fmt^.Backgroundcolor <> scWhite) then
Attrib3 := Attrib3 or $80;
end;
@@ -1131,51 +1203,56 @@ end;
Is called from all writing methods of cell contents. }
procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell;
XFIndex: Word);
type
TCellFmtRecord = packed record
XFIndex_Locked_Hidden: Byte;
Format_Font: Byte;
Align_Border_BkGr: Byte;
end;
var
b: Byte;
rec: TCellFmtRecord;
fmt: PsCellFormat;
w: Word;
begin
if ACell^.UsedFormattingFields = [] then
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
rec.XFIndex_Locked_Hidden := 0; // to silence the compiler...
FillChar(rec, SizeOf(rec), 0);
if fmt^.UsedFormattingFields <> [] then
begin
AStream.WriteByte($0);
AStream.WriteByte($0);
AStream.WriteByte($0);
Exit;
// 1st byte:
// Mask $3F: Index to XF record
// Mask $40: 1 = Cell is locked
// Mask $80: 1 = Formula is hidden
rec.XFIndex_Locked_Hidden := Min(XFIndex, $3F) and $3F;
// AStream.WriteByte(Min(XFIndex, $3F) and $3F);
// 2nd byte:
// Mask $3F: Index to FORMAT record
// Mask $C0: Index to FONT record
w := fmt^.FontIndex shr 6; // was shl --> MUST BE shr! // ??????????????????????
rec.Format_Font := Lo(w);
// AStream.WriteByte(b);
// 3rd byte
// Mask $07: horizontal alignment
// Mask $08: Cell has left border
// Mask $10: Cell has right border
// Mask $20: Cell has top border
// Mask $40: Cell has bottom border
// Mask $80: Cell has shaded background
if uffHorAlign in fmt^.UsedFormattingFields then
rec.Align_Border_BkGr := ord(fmt^.HorAlignment);
if uffBorder in fmt^.UsedFormattingFields then begin
if cbNorth in fmt^.Border then rec.Align_Border_BkGr := rec.Align_Border_BkGr or $20;
if cbWest in fmt^.Border then rec.Align_Border_BkGr := rec.Align_Border_BkGr or $08;
if cbEast in fmt^.Border then rec.Align_Border_BkGr := rec.Align_Border_BkGr or $10;
if cbSouth in fmt^.Border then rec.Align_Border_BkGr := rec.Align_Border_BkGr or $40;
end;
if uffBackgroundColor in fmt^.UsedFormattingFields then
rec.Align_Border_BkGr := rec.Align_Border_BkGr or $80;
end;
// 1st byte:
// Mask $3F: Index to XF record
// Mask $40: 1 = Cell is locked
// Mask $80: 1 = Formula is hidden
AStream.WriteByte(Min(XFIndex, $3F) and $3F);
// 2nd byte:
// Mask $3F: Index to FORMAT record
// Mask $C0: Index to FONT record
w := ACell.FontIndex shr 6; // was shl --> MUST BE shr!
b := Lo(w);
//b := ACell.FontIndex shl 6;
AStream.WriteByte(b);
// 3rd byte
// Mask $07: horizontal alignment
// Mask $08: Cell has left border
// Mask $10: Cell has right border
// Mask $20: Cell has top border
// Mask $40: Cell has bottom border
// Mask $80: Cell has shaded background
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;
if uffBackgroundColor in ACell^.UsedFormattingFields then
b := b or $80;
AStream.WriteByte(b);
AStream.WriteBuffer(rec, SizeOf(rec));
end;
{
@@ -1235,7 +1312,7 @@ end;
procedure TsSpreadBIFF2Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
var
firstRow, lastRow, firstCol, lastCol: Cardinal;
rec: TBIFF2DimensionsRecord;
rec: TBIFF2_DimensionsRecord;
begin
{ Determine sheet size }
GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol);
@@ -1282,7 +1359,7 @@ begin
WriteBOF(AStream);
WriteFonts(AStream);
WriteFormatCount(AStream);
WriteFormats(AStream);
WriteNumFormats(AStream);
WriteXFRecords(AStream);
WriteColWidths(AStream);
WriteDimensions(AStream, FWorksheet);
@@ -1376,6 +1453,86 @@ begin
AStream.WriteDWord(0);
end;
procedure TsSpreadBIFF2Writer.WriteXF(AStream: TStream;
AFormatRecord: PsCellFormat; XFType_Prot: Byte = 0);
var
rec: TBIFF2_XFRecord;
b: Byte;
j: Integer;
clr: TsColorvalue;
begin
Unused(XFType_Prot);
{ BIFF Record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_XF);
rec.RecordSize := WordToLE(SizeOf(TBIFF2_XFRecord) - 2*SizeOf(word));
{ Index to FONT record }
rec.FontIndex := 0;
if (AFormatRecord <> nil) then
begin
if (uffBold in AFormatRecord^.UsedFormattingFields) then
rec.FontIndex := 1
else
if (uffFont in AFormatRecord^.UsedFormattingFields) then
rec.FontIndex := AFormatRecord^.FontIndex;
end;
{ Not used byte }
rec.NotUsed := 0;
{ Number format index and cell flags
Bit Mask Contents
----- ---- --------------------------------
5-0 $3F Index to (number) FORMAT record
6 $40 1 = Cell is locked
7 $80 1 = Formula is hidden }
rec.NumFormatIndex_Flags := 0;
if (AFormatRecord <> nil) and (uffNumberFormat in AFormatRecord^.UsedFormattingFields) then
begin
// The number formats in the FormatList are still in fpc dialect
// They will be converted to Excel syntax immediately before writing.
j := NumFormatList.Find(AFormatRecord^.NumberFormat, AFormatRecord^.NumberFormatStr);
if j > -1 then
rec.NumFormatIndex_Flags := NumFormatList[j].Index;
// Cell flags not used, so far...
end;
{Horizontal alignment, border style, and background
Bit Mask Contents
--- ---- ------------------------------------------------
2-0 $07 XF_HOR_ALIGN – Horizontal alignment (0=General, 1=Left, 2=Centred, 3=Right)
3 $08 1 = Cell has left black border
4 $10 1 = Cell has right black border
5 $20 1 = Cell has top black border
6 $40 1 = Cell has bottom black border
7 $80 1 = Cell has shaded background }
b := 0;
if (AFormatRecord <> nil) then
begin
if (uffHorAlign in AFormatRecord^.UsedFormattingFields) then
b := b + byte(AFormatRecord^.HorAlignment);
if (uffBorder in AFormatRecord^.UsedFormattingFields) then
begin
if cbWest in AFormatRecord^.Border then b := b or $08;
if cbEast in AFormatRecord^.Border then b := b or $10;
if cbNorth in AFormatRecord^.Border then b := b or $20;
if cbSouth in AFormatRecord^.Border then b := b or $40;
end;
if (uffBackgroundColor in AFormatRecord^.UsedFormattingFields) then
begin
clr := Workbook.GetPaletteColor(AFormatRecord^.BackgroundColor);
if clr <> $FFFFFF then
b := b or $80;
end;
end;
rec.HorAlign_Border_BkGr:= b;
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
(*
procedure TsSpreadBIFF2Writer.WriteXF(AStream: TStream;
AFontIndex, AFormatIndex: byte; ABorders: TsCellBorders = [];
AHorAlign: TsHorAlignment = haLeft; AddBackground: Boolean = false);
@@ -1449,28 +1606,28 @@ end;
procedure TsSpreadBIFF2Writer.WriteXFRecords(AStream: TStream);
begin
WriteXF(AStream, 0, 0); // XF0
WriteXF(AStream, 0, 0); // XF1
WriteXF(AStream, 0, 0); // XF2
WriteXF(AStream, 0, 0); // XF3
WriteXF(AStream, 0, 0); // XF4
WriteXF(AStream, 0, 0); // XF5
WriteXF(AStream, 0, 0); // XF6
WriteXF(AStream, 0, 0); // XF7
WriteXF(AStream, 0, 0); // XF8
WriteXF(AStream, 0, 0); // XF9
WriteXF(AStream, 0, 0); // XF10
WriteXF(AStream, 0, 0); // XF11
WriteXF(AStream, 0, 0); // XF12
WriteXF(AStream, 0, 0); // XF13
WriteXF(AStream, 0, 0); // XF14
WriteXF(AStream, 0, 0); // XF15 - Default, no formatting
WriteXFRecord(AStream, 0, 0); // XF0
WriteXFRecord(AStream, 0, 0); // XF1
WriteXFRecord(AStream, 0, 0); // XF2
WriteXFRecord(AStream, 0, 0); // XF3
WriteXFRecord(AStream, 0, 0); // XF4
WriteXFRecord(AStream, 0, 0); // XF5
WriteXFRecord(AStream, 0, 0); // XF6
WriteXFRecord(AStream, 0, 0); // XF7
WriteXFRecord(AStream, 0, 0); // XF8
WriteXFRecord(AStream, 0, 0); // XF9
WriteXFRecord(AStream, 0, 0); // XF10
WriteXFRecord(AStream, 0, 0); // XF11
WriteXFRecord(AStream, 0, 0); // XF12
WriteXFRecord(AStream, 0, 0); // XF13
WriteXFRecord(AStream, 0, 0); // XF14
WriteXFRecord(AStream, 0, 0); // XF15 - Default, no formatting
// Add all further non-standard/built-in formatting styles
ListAllFormattingStyles;
WriteXFFieldsForFormattingStyles(AStream);
end;
*)
{
Writes an Excel 2 BOF record
@@ -1526,7 +1683,7 @@ begin
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
AStream.WriteWord(WordToLE(4 + 1 + Len * Sizeof(AnsiChar)));
AStream.WriteWord(WordToLE(4 + 1 + Len * SizeOf(AnsiChar)));
{ Height of the font in twips = 1/20 of a point }
AStream.WriteWord(WordToLE(round(font.Size*20)));
@@ -1561,8 +1718,8 @@ begin
WriteFont(AStream, i);
end;
procedure TsSpreadBiff2Writer.WriteFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer);
procedure TsSpreadBiff2Writer.WriteNumFormat(AStream: TStream;
ANumFormatData: TsNumFormatData; AListIndex: Integer);
type
TNumFormatRecord = packed record
RecordID: Word;
@@ -1575,7 +1732,7 @@ var
rec: TNumFormatRecord;
buf: array of byte;
begin
Unused(AFormatData);
Unused(ANumFormatData);
s := NumFormatList.FormatStringForWriting(AListIndex);
len := Length(s);
@@ -1748,7 +1905,7 @@ end;
procedure TsSpreadBIFF2Writer.WriteBool(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
var
rec: TBIFF2BoolErrRecord;
rec: TBIFF2_BoolErrRecord;
xf: Integer;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
@@ -1781,7 +1938,7 @@ end;
procedure TsSpreadBIFF2Writer.WriteError(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell);
var
rec: TBIFF2BoolErrRecord;
rec: TBIFF2_BoolErrRecord;
xf: Integer;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
@@ -1874,7 +2031,7 @@ const
var
L: Byte;
AnsiText: ansistring;
rec: TBIFF2LabelRecord;
rec: TBIFF2_LabelRecord;
buf: array of byte;
var
xf: Word;
@@ -1937,7 +2094,7 @@ procedure TsSpreadBIFF2Writer.WriteNumber(AStream: TStream; const ARow,
ACol: Cardinal; const AValue: double; ACell: PCell);
var
xf: Word;
rec: TBIFF2NumberRecord;
rec: TBIFF2_NumberRecord;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;

View File

@@ -115,21 +115,24 @@ type
procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TsFont);
procedure WriteFonts(AStream: TStream);
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); override;
procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: string; ACell: PCell); override;
procedure WriteNumFormat(AStream: TStream; ANumFormatData: TsNumFormatData;
AListIndex: Integer); override;
procedure WriteStringRecord(AStream: TStream; AString: String); override;
procedure WriteStyle(AStream: TStream);
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
{
procedure WriteXF(AStream: TStream; AFontIndex: Word;
AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders;
const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault;
AVertAlignment: TsVertAlignment = vaDefault; AWordWrap: Boolean = false;
AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver);
procedure WriteXFFieldsForFormattingStyles(AStream: TStream);
procedure WriteXFRecords(AStream: TStream);
}
procedure WriteXF(AStream: TStream; AFormatRecord: PsCellFormat;
XFType_Prot: Byte = 0); override;
public
{ General writing methods }
procedure WriteToFile(const AFileName: string;
@@ -274,6 +277,7 @@ const
{ XF substructures }
{ XF substructures --- see xlscommon! }
MASK_XF_ORIENTATION = $03;
XF_ROTATION_HORIZONTAL = 0;
XF_ROTATION_STACKED = 1;
XF_ROTATION_90DEG_CCW = 2;
@@ -304,7 +308,7 @@ const
);
type
TBIFF5DimensionsRecord = packed record
TBIFF5_DimensionsRecord = packed record
RecordID: Word;
RecordSize: Word;
FirstRow: Word;
@@ -314,7 +318,7 @@ type
NotUsed: Word;
end;
TBIFF5LabelRecord = packed record
TBIFF5_LabelRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -323,6 +327,18 @@ type
TextLen: Word;
end;
TBIFF5_XFRecord = packed record
RecordID: Word;
RecordSize: Word;
FontIndex: Word;
NumFormatIndex: Word;
XFType_Prot_ParentXF: Word;
Align_TextBreak: Byte;
TextOrient_UnusedAttrib: Byte;
Border_BkGr1: DWord;
Border_BkGr2: DWord;
end;
{ TsSpreadBIFF5Writer }
@@ -393,7 +409,7 @@ begin
WriteCodepage(AStream, WorkBookEncoding);
WriteWindow1(AStream);
WriteFonts(AStream);
WriteFormats(AStream);
WriteNumFormats(AStream);
WritePalette(AStream);
WriteXFRecords(AStream);
WriteStyle(AStream);
@@ -530,7 +546,7 @@ end;
}
procedure TsSpreadBIFF5Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
var
rec: TBIFF5DimensionsRecord;
rec: TBIFF5_DimensionsRecord;
firstCol, lastCol, firstRow, lastRow: Cardinal;
begin
{ Determine sheet size }
@@ -658,8 +674,8 @@ end;
* DESCRIPTION: Writes an Excel 5 FORMAT record
*
*******************************************************************}
procedure TsSpreadBiff5Writer.WriteFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer);
procedure TsSpreadBiff5Writer.WriteNumFormat(AStream: TStream;
ANumFormatData: TsNumFormatData; AListIndex: Integer);
type
TNumFormatRecord = packed record
RecordID: Word;
@@ -673,7 +689,7 @@ var
rec: TNumFormatRecord;
buf: array of byte;
begin
if (AFormatData = nil) or (AFormatData.FormatString = '') then
if (ANumFormatData = nil) or (ANumFormatData.FormatString = '') then
exit;
s := UTF8ToAnsi(NumFormatList.FormatStringForWriting(AListIndex));
@@ -684,7 +700,7 @@ begin
rec.RecordSize := WordToLE(2 + 1 + len * SizeOf(AnsiChar));
{ Format index }
rec.FormatIndex := WordToLE(AFormatData.Index);
rec.FormatIndex := WordToLE(ANumFormatData.Index);
{ Format string }
{ Length in 1 byte }
@@ -751,7 +767,7 @@ const
var
L: Word;
AnsiValue: ansistring;
rec: TBIFF5LabelRecord;
rec: TBIFF5_LabelRecord;
buf: array of byte;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
@@ -905,7 +921,7 @@ begin
{ Grid line RGB colour }
AStream.WriteDWord(DWordToLE(0));
end;
(*
{*******************************************************************
* TsSpreadBIFF5Writer.WriteXF ()
*
@@ -981,8 +997,114 @@ begin
if cbEast in ABorders then dw2 := dw2 or ((DWord(ABorderStyles[cbEast].LineStyle)+1) shl 6);
AStream.WriteDWord(DWordToLE(dw1));
AStream.WriteDWord(DWordToLE(dw2));
end;
end; *)
procedure TsSpreadBIFF5Writer.WriteXF(AStream: TStream;
AFormatRecord: PsCellFormat; XFType_Prot: Byte = 0);
var
rec: TBIFF5_XFRecord;
j: Integer;
b: Byte;
dw1, dw2: DWord;
begin
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_XF);
rec.RecordSize := WordToLE(SizeOf(TBIFF5_XFRecord) - 2*SizeOf(Word));
{ Index to font record }
rec.FontIndex := 0;
if (AFormatRecord <> nil) then begin
if (uffBold in AFormatRecord^.UsedFormattingFields) then
rec.FontIndex := 1
else
if (uffFont in AFormatRecord^.UsedFormattingFields) then
rec.FontIndex := AFormatRecord^.FontIndex;
end;
rec.FontIndex := WordToLE(rec.FontIndex);
{ Index to number format }
rec.NumFormatIndex := 0;
if (AFormatRecord <> nil) and (uffNumberFormat in AFormatRecord^.UsedFormattingFields)
then begin
// The number formats in the FormatList are still in fpc dialect
// They will be converted to Excel syntax immediately before writing.
j := NumFormatList.Find(AFormatRecord^.NumberFormat, AFormatRecord^.NumberFormatStr);
if j > -1 then
rec.NumFormatIndex := NumFormatList[j].Index;
end;
rec.NumFormatIndex := WordToLE(rec.NumFormatIndex);
{ XF type, cell protection and parent style XF }
rec.XFType_Prot_ParentXF := XFType_Prot and MASK_XF_TYPE_PROT;
if XFType_Prot and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_PARENT;
{ Text alignment and text break }
if AFormatRecord = nil then
b := MASK_XF_VERT_ALIGN_BOTTOM
else
begin
b := 0;
if (uffHorAlign in AFormatRecord^.UsedFormattingFields) then
case AFormatRecord^.HorAlignment 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;
haDefault: ;
end;
// Since the default vertical alignment is vaDefault but "0" corresponds
// to vaTop, we alwys have to write the vertical alignment.
case AFormatRecord^.VertAlignment 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 (uffWordWrap in AFormatRecord^.UsedFormattingFields) then
b := b or MASK_XF_TEXTWRAP;
end;
rec.Align_TextBreak := b;
{ Text rotation }
rec.TextOrient_UnusedAttrib := 0;
if (AFormatRecord <> nil) and (uffTextRotation in AFormatRecord^.UsedFormattingFields)
then rec.TextOrient_UnusedAttrib := TEXT_ROTATIONS[AFormatRecord^.TextRotation];
{ Cell border lines and background area }
dw1 := 0;
dw2 := 0;
if (AFormatRecord <> nil) then
begin
if (uffBackgroundColor in AFormatRecord^.UsedFormattingFields) then
begin
// Background color
dw1 := dw1 or (FixColor(AFormatRecord^.BackgroundColor) and $0000007F);
dw1 := dw1 or (MASK_XF_FILL_PATT_SOLID shl 16);
end;
// Border lines
if (uffBorder in AFormatRecord^.UsedFormattingFields) then
begin
dw1 := dw1 or (AFormatRecord^.BorderStyles[cbSouth].Color shl 25); // Bottom line color
dw2 := (FixColor(AFormatRecord^.BorderStyles[cbNorth].Color) shl 9) or // Top line color
(FixColor(AFormatRecord^.BorderStyles[cbWest].Color) shl 16) or // Left line color
(FixColor(AFormatRecord^.BorderStyles[cbEast].Color) shl 23); // Right line color
if cbSouth in AFormatRecord^.Border then
dw1 := dw1 or ((DWord(AFormatRecord^.BorderStyles[cbSouth].LineStyle)+1) shl 22);
if cbNorth in AFormatRecord^.Border then
dw2 := dw2 or (DWord(AFormatRecord^.BorderStyles[cbNorth].LineStyle)+1);
if cbWest in AFormatRecord^.Border then
dw2 := dw2 or ((DWord(AFormatRecord^.BorderStyles[cbWest].LineStyle)+1) shl 3);
if cbEast in AFormatRecord^.Border then
dw2 := dw2 or ((DWord(AFormatRecord^.BorderStyles[cbEast].LineStyle)+1) shl 6);
end;
end;
rec.Border_BkGr1 := dw1;
rec.Border_BkGr2 := dw2;
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
(*
procedure TsSpreadBIFF5Writer.WriteXFFieldsForFormattingStyles(AStream: TStream);
var
i, j: Integer;
@@ -1040,7 +1162,52 @@ begin
end;
procedure TsSpreadBIFF5Writer.WriteXFRecords(AStream: TStream);
var
i: Integer;
fmt: TsCellFormat;
begin
// XF0
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF1
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF2
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF3
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF4
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF5
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF6
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF7
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF8
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF9
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF10
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF11
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF12
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF13
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF14
WriteXFRecord(AStream, MASK_XF_TYPE_PROT_STYLE_XF, nil);
// XF15 - Default, no formatting
WriteXFRecord(AStream, 0, nil);
// Add all further non-standard format records
// The first style was already added --> begin loop with 1
for i:=1 to FWorkbook.GetNumFormatRecords-1 do begin
fmt := FWorkbook.GetFormatRecord(i);
WriteXFRecord(AStream, 0, @fmt);
end;
{
// XF0
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF1
@@ -1077,8 +1244,9 @@ begin
// Add all further non-standard/built-in formatting styles
ListAllFormattingStyles;
WriteXFFieldsForFormattingStyles(AStream);
}
end;
*)
{ TsSpreadBIFF5Reader }
@@ -1340,6 +1508,7 @@ begin
end;
procedure TsSpreadBIFF5Reader.ReadXF(AStream: TStream);
{
type
TXFRecord = packed record // see p. 224
FontIndex: Word; // Offset 0, Size 2
@@ -1349,109 +1518,136 @@ type
XFRotation: Byte; // Offset 7, Size 1
Border_Background_1: DWord; // Offset 8, Size 4
Border_Background_2: DWord; // Offset 12, Size 4
end;
end; }
var
lData: TXFListData;
xf: TXFRecord;
rec: TBIFF5_XFRecord;
fmt: TsCellFormat;
nfidx: Integer;
i: Integer;
nfdata: TsNumFormatData;
//lData: TXFListData;
//xf: TXFRecord;
b: Byte;
dw: DWord;
fill: Word;
begin
xf.FontIndex := 0; // to silence the compiler...
InitFormatRecord(fmt);
fmt.ID := FCellFormatList.Count;
// Read the complete xf record into a buffer
AStream.ReadBuffer(xf, SizeOf(xf));
lData := TXFListData.Create;
rec.FontIndex := 0; // to silence the compiler...
AStream.ReadBuffer(rec.FontIndex, SizeOf(rec) - 2*SizeOf(Word));
// Font index
lData.FontIndex := WordLEToN(xf.FontIndex);
fmt.FontIndex := WordLEToN(rec.FontIndex);
if fmt.FontIndex = 1 then
Include(fmt.UsedFormattingFields, uffBold)
else if fmt.FontIndex > 1 then
Include(fmt.UsedFormattingFields, uffFont);
// Format index
lData.FormatIndex := WordLEToN(xf.FormatIndex);
// Number format index
nfidx := WordLEToN(rec.NumFormatIndex);
i := NumFormatList.FindByIndex(nfidx);
if i > -1 then begin
nfdata := NumFormatList.Items[i];
fmt.NumberFormat := nfdata.NumFormat;
fmt.NumberFormatStr := nfdata.FormatString;
if nfdata.NumFormat <> nfGeneral then
Include(fmt.UsedFormattingFields, uffNumberFormat);
end;
// Horizontal text alignment
b := xf.Align_TextBreak AND MASK_XF_HOR_ALIGN;
b := rec.Align_TextBreak AND MASK_XF_HOR_ALIGN;
if (b <= ord(High(TsHorAlignment))) then
lData.HorAlignment := TsHorAlignment(b)
else
lData.HorAlignment := haDefault;
begin
fmt.HorAlignment := TsHorAlignment(b);
if fmt.HorAlignment <> haDefault then
Include(fmt.UsedFormattingFields, uffHorAlign);
end;
// Vertical text alignment
b := (xf.Align_TextBreak AND MASK_XF_VERT_ALIGN) shr 4;
b := (rec.Align_TextBreak AND MASK_XF_VERT_ALIGN) shr 4;
if (b + 1 <= ord(high(TsVertAlignment))) then
begin
lData.VertAlignment := tsVertAlignment(b + 1); // + 1 due to vaDefault
fmt.VertAlignment := TsVertAlignment(b + 1); // + 1 due to vaDefault
// Unfortunately BIFF does not provide a "default" vertical alignment code.
// Without the following correction "non-formatted" cells would always have
// the uffVertAlign FormattingField set which contradicts the statement of
// not being formatted.
if lData.VertAlignment = vaBottom then
lData.VertAlignment := vaDefault;
end
else
lData.VertAlignment := vaDefault;
// Word wrap
lData.WordWrap := (xf.Align_TextBreak and MASK_XF_TEXTWRAP) <> 0;
// Text rotation
case xf.XFRotation of
XF_ROTATION_HORIZONTAL : lData.TextRotation := trHorizontal;
XF_ROTATION_90DEG_CCW : lData.TextRotation := rt90DegreeCounterClockwiseRotation;
XF_ROTATION_90DEG_CW : lData.TextRotation := rt90DegreeClockwiseRotation;
XF_ROTATION_STACKED : lData.TextRotation := rtStacked;
if fmt.VertAlignment = vaBottom then
fmt.VertAlignment := vaDefault;
if fmt.VertAlignment <> vaDefault then
Include(fmt.UsedFormattingFields, uffVertAlign);
end;
// Word wrap
if (rec.Align_TextBreak and MASK_XF_TEXTWRAP) <> 0 then
Include(fmt.UsedFormattingFields, uffWordwrap);
// Text rotation
case rec.TextOrient_UnusedAttrib and MASK_XF_ORIENTATION of
XF_ROTATION_HORIZONTAL : fmt.TextRotation := trHorizontal;
XF_ROTATION_90DEG_CCW : fmt.TextRotation := rt90DegreeCounterClockwiseRotation;
XF_ROTATION_90DEG_CW : fmt.TextRotation := rt90DegreeClockwiseRotation;
XF_ROTATION_STACKED : fmt.TextRotation := rtStacked;
end;
if fmt.TextRotation <> trHorizontal then
Include(fmt.UsedFormattingFields, uffTextRotation);
// Cell borders and background
xf.Border_Background_1 := DWordLEToN(xf.Border_Background_1);
xf.Border_Background_2 := DWordLEToN(xf.Border_Background_2);
lData.Borders := [];
rec.Border_BkGr1 := DWordLEToN(rec.Border_BkGr1);
rec.Border_BkGr2 := DWordLEToN(rec.Border_BkGr2);
// The 4 masked bits encode the line style of the border line. 0 = no line.
// The case of "no line" is not included in the TsLineStyle enumeration.
// --> correct by subtracting 1!
dw := xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM;
dw := rec.Border_BkGr1 and MASK_XF_BORDER_BOTTOM;
if dw <> 0 then
begin
Include(lData.Borders, cbSouth);
lData.BorderStyles[cbSouth].LineStyle := TsLineStyle(dw shr 22 - 1);
Include(fmt.Border, cbSouth);
fmt.BorderStyles[cbSouth].LineStyle := TsLineStyle(dw shr 22 - 1);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_2 and MASK_XF_BORDER_LEFT;
dw := rec.Border_BkGr2 and MASK_XF_BORDER_LEFT;
if dw <> 0 then
begin
Include(lData.Borders, cbWest);
lData.BorderStyles[cbWest].LineStyle := TsLineStyle(dw shr 3 - 1);
Include(fmt.Border, cbWest);
fmt.BorderStyles[cbWest].LineStyle := TsLineStyle(dw shr 3 - 1);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_2 and MASK_XF_BORDER_RIGHT;
dw := rec.Border_BkGr2 and MASK_XF_BORDER_RIGHT;
if dw <> 0 then
begin
Include(lData.Borders, cbEast);
lData.BorderStyles[cbEast].LineStyle := TsLineStyle(dw shr 6 - 1);
Include(fmt.Border, cbEast);
fmt.BorderStyles[cbEast].LineStyle := TsLineStyle(dw shr 6 - 1);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_2 and MASK_XF_BORDER_TOP;
dw := rec.Border_BkGr2 and MASK_XF_BORDER_TOP;
if dw <> 0 then
begin
Include(lData.Borders, cbNorth);
lData.BorderStyles[cbNorth].LineStyle := TsLineStyle(dw - 1);
Include(fmt.Border, cbNorth);
fmt.BorderStyles[cbNorth].LineStyle := TsLineStyle(dw - 1);
Include(fmt.UsedFormattingFields, uffBorder);
end;
// Border line colors
lData.BorderStyles[cbWest].Color := (xf.Border_Background_2 and MASK_XF_BORDER_LEFT_COLOR) shr 16;
lData.BorderStyles[cbEast].Color := (xf.Border_Background_2 and MASK_XF_BORDER_RIGHT_COLOR) shr 23;
lData.BorderStyles[cbNorth].Color := (xf.Border_Background_2 and MASK_XF_BORDER_TOP_COLOR) shr 9;
lData.BorderStyles[cbSouth].Color := (xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM_COLOR) shr 25;
fmt.BorderStyles[cbWest].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_LEFT_COLOR) shr 16;
fmt.BorderStyles[cbEast].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_RIGHT_COLOR) shr 23;
fmt.BorderStyles[cbNorth].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_TOP_COLOR) shr 9;
fmt.BorderStyles[cbSouth].Color := (rec.Border_BkGr1 and MASK_XF_BORDER_BOTTOM_COLOR) shr 25;
// Background fill style
fill := (xf.Border_Background_1 and MASK_XF_BKGR_FILLPATTERN) shr 16;
fill := (rec.Border_BkGr1 and MASK_XF_BKGR_FILLPATTERN) shr 16;
// Background color
if fill = 0 then
lData.BackgroundColor := scTransparent
else
lData.BackgroundColor := xf.Border_Background_1 and MASK_XF_BKGR_PATTERN_COLOR;
fmt.BackgroundColor := scTransparent
else begin
fmt.BackgroundColor := rec.Border_BkGr1 and MASK_XF_BKGR_PATTERN_COLOR;
Include(fmt.UsedFormattingFields, uffBackgroundColor);
end;
// Add the XF to the list
FXFList.Add(lData);
FCellFormatList.Add(fmt);
end;
procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
@@ -1582,7 +1778,7 @@ end;
procedure TsSpreadBIFF5Reader.ReadLabel(AStream: TStream);
var
rec: TBIFF5LabelRecord;
rec: TBIFF5_LabelRecord;
L: Word;
ARow, ACol: Cardinal;
XF: WORD;
@@ -1592,7 +1788,7 @@ begin
rec.Row := 0; // to silence the compiler...
{ Read entire record, starting at Row, except for string data }
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF5LabelRecord) - 2*SizeOf(Word));
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF5_LabelRecord) - 2*SizeOf(Word));
ARow := WordLEToN(rec.Row);
ACol := WordLEToN(rec.Col);
XF := WordLEToN(rec.XFIndex);

View File

@@ -111,7 +111,7 @@ type
TsSpreadBIFF8Writer = class(TsSpreadBIFFWriter)
private
// Writes index to XF record according to cell's formatting
procedure WriteXFFieldsForFormattingStyles(AStream: TStream);
//procedure WriteXFFieldsForFormattingStyles(AStream: TStream);
protected
{ Record writing methods }
procedure WriteBOF(AStream: TStream; ADataType: Word);
@@ -120,12 +120,12 @@ type
procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TsFont);
procedure WriteFonts(AStream: TStream);
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); override;
procedure WriteIndex(AStream: TStream);
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: string; ACell: PCell); override;
procedure WriteMergedCells(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteNumFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); override;
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
AFlags: TsRelFlags): word; override;
function WriteRPNCellOffset(AStream: TStream; ARowOffset, AColOffset: Integer;
@@ -136,12 +136,15 @@ type
procedure WriteStringRecord(AStream: TStream; AString: string); override;
procedure WriteStyle(AStream: TStream);
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
(*
procedure WriteXF(AStream: TStream; AFontIndex: Word;
AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders;
const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault;
AVertAlignment: TsVertAlignment = vaDefault; AWordWrap: Boolean = false;
AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver);
procedure WriteXFRecords(AStream: TStream);
*)
procedure WriteXF(AStream: TStream; AFormatRecord: PsCellFormat;
XFType_Prot: Byte = 0); override;
public
constructor Create(AWorkbook: TsWorkbook); override;
{ General writing methods }
@@ -290,7 +293,7 @@ const
);
type
TBIFF8DimensionsRecord = packed record
TBIFF8_DimensionsRecord = packed record
RecordID: Word;
RecordSize: Word;
FirstRow: DWord;
@@ -300,7 +303,7 @@ type
NotUsed: Word;
end;
TBIFF8LabelRecord = packed record
TBIFF8_LabelRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -310,7 +313,7 @@ type
TextFlags: Byte;
end;
TBIFF8LabelSSTRecord = packed record
TBIFF8_LabelSSTRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
@@ -319,6 +322,21 @@ type
SSTIndex: DWord;
end;
TBIFF8_XFRecord = packed record
RecordID: Word;
RecordSize: Word;
FontIndex: Word;
NumFormatIndex: Word;
XFType_Prot_ParentXF: Word;
Align_TextBreak: Byte;
TextRotation: Byte;
Indent_Shrink_TextDir: Byte;
UsedAttrib: Byte;
Border_BkGr1: DWord;
Border_BkGr2: DWord;
BkGr3: Word;
end;
{ TsSpreadBIFF8Writer }
@@ -326,7 +344,7 @@ constructor TsSpreadBIFF8Writer.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
end;
(*
procedure TsSpreadBIFF8Writer.WriteXFFieldsForFormattingStyles(AStream: TStream);
var
i, j: Integer;
@@ -383,7 +401,7 @@ begin
lBackgroundColor);
end;
end;
*)
{*******************************************************************
* TsSpreadBIFF8Writer.WriteToFile ()
*
@@ -449,7 +467,7 @@ begin
WriteWindow1(AStream);
WriteFonts(AStream);
WriteFormats(AStream);
WriteNumFormats(AStream);
WritePalette(AStream);
WriteXFRecords(AStream);
WriteStyle(AStream);
@@ -601,7 +619,7 @@ end;
procedure TsSpreadBIFF8Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
var
firstRow, lastRow, firstCol, lastCol: Cardinal;
rec: TBIFF8DimensionsRecord;
rec: TBIFF8_DimensionsRecord;
begin
{ Determine sheet size }
GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol);
@@ -724,7 +742,7 @@ begin
WriteFont(AStream, Workbook.GetFont(i));
end;
procedure TsSpreadBiff8Writer.WriteFormat(AStream: TStream;
procedure TsSpreadBiff8Writer.WriteNumFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer);
type
TNumFormatRecord = packed record
@@ -921,7 +939,7 @@ const
var
L: Word;
WideValue: WideString;
rec: TBIFF8LabelRecord;
rec: TBIFF8_LabelRecord;
buf: array of byte;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
@@ -1113,6 +1131,145 @@ end;
*
*
*******************************************************************}
procedure TsSpreadBIFF8Writer.WriteXF(AStream: TStream;
AFormatRecord: PsCellFormat; XFType_Prot: Byte = 0);
var
rec: TBIFF8_XFRecord;
j: Integer;
b: Byte;
dw1, dw2: DWord;
begin
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_XF);
rec.RecordSize := WordToLE(SizeOf(TBIFF8_XFRecord) - 2*SizeOf(Word));
{ Index to font record }
rec.FontIndex := 0;
if (AFormatRecord <> nil) then begin
if (uffBold in AFormatRecord^.UsedFormattingFields) then
rec.FontIndex := 1
else
if (uffFont in AFormatRecord^.UsedFormattingFields) then
rec.FontIndex := AFormatRecord^.FontIndex;
end;
rec.FontIndex := WordToLE(rec.FontIndex);
{ Index to number format }
rec.NumFormatIndex := 0;
if (AFormatRecord <> nil) and (uffNumberFormat in AFormatRecord^.UsedFormattingFields)
then begin
// The number formats in the FormatList are still in fpc dialect
// They will be converted to Excel syntax immediately before writing.
j := NumFormatList.Find(AFormatRecord^.NumberFormat, AFormatRecord^.NumberFormatStr);
if j > -1 then
rec.NumFormatIndex := NumFormatList[j].Index;
end;
rec.NumFormatIndex := WordToLE(rec.NumFormatIndex);
{ XF type, cell protection and parent style XF }
rec.XFType_Prot_ParentXF := XFType_Prot and MASK_XF_TYPE_PROT;
if XFType_Prot and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_PARENT;
{ Text alignment and text break }
if AFormatRecord = nil then
b := MASK_XF_VERT_ALIGN_BOTTOM
else
begin
b := 0;
if (uffHorAlign in AFormatRecord^.UsedFormattingFields) then
case AFormatRecord^.HorAlignment of
haDefault: ;
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;
// Since the default vertical alignment is vaDefault but "0" corresponds
// to vaTop, we alwys have to write the vertical alignment.
case AFormatRecord^.VertAlignment 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 (uffWordWrap in AFormatRecord^.UsedFormattingFields) then
b := b or MASK_XF_TEXTWRAP;
end;
rec.Align_TextBreak := b;
{ Text rotation }
rec.TextRotation := 0;
if (AFormatRecord <> nil) and (uffTextRotation in AFormatRecord^.UsedFormattingFields)
then rec.TextRotation := TEXT_ROTATIONS[AFormatRecord^.TextRotation];
{ Indentation, shrink, merge and text direction:
see "Excel97-2007BinaryFileFormat(xls)Specification.pdf", p281 ff
Bits 0-3: Indent value
Bit 4: Shrink to fit
Bit 5: MergeCell
Bits 6-7: Reading direction }
rec.Indent_Shrink_TextDir := 0;
{ Used attributes }
rec.UsedAttrib :=
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;
{ Cell border lines and background area }
dw1 := 0;
dw2 := 0;
rec.BkGr3 := 0;
if (AFormatRecord <> nil) and (uffBorder in AFormatRecord^.UsedFormattingFields) then
begin
// Left and right line colors
dw1 := AFormatRecord^.BorderStyles[cbWest].Color shl 16 +
AFormatRecord^.BorderStyles[cbEast].Color shl 23;
// Border line styles
if cbWest in AFormatRecord^.Border then
dw1 := dw1 or (DWord(AFormatRecord^.BorderStyles[cbWest].LineStyle)+1);
if cbEast in AFormatRecord^.Border then
dw1 := dw1 or ((DWord(AFormatRecord^.BorderStyles[cbEast].LineStyle)+1) shl 4);
if cbNorth in AFormatRecord^.Border then
dw1 := dw1 or ((DWord(AFormatRecord^.BorderStyles[cbNorth].LineStyle)+1) shl 8);
if cbSouth in AFormatRecord^.Border then
dw1 := dw1 or ((DWord(AFormatRecord^.BorderStyles[cbSouth].LineStyle)+1) shl 12);
if cbDiagDown in AFormatRecord^.Border then
dw1 := dw1 or $40000000;
if cbDiagUp in AFormatRecord^.Border then
dw1 := dw1 or $80000000;
// Top, bottom and diagonal line colors
dw2 := FixColor(AFormatRecord^.BorderStyles[cbNorth].Color) +
FixColor(AFormatRecord^.BorderStyles[cbSouth].Color) shl 7 +
FixColor(AFormatRecord^.BorderStyles[cbDiagUp].Color) shl 14;
// In BIFF8 both diagonals have the same color - we use the color of the up-diagonal.
// Diagonal line style
if (AFormatRecord^.Border * [cbDiagUp, cbDiagDown] <> []) then
dw2 := dw2 or ((DWord(AFormatRecord^.BorderStyles[cbDiagUp].LineStyle)+1) shl 21);
// In BIFF8 both diagonals have the same line style - we use the color of the up-diagonal.
end;
if (AFormatRecord <> nil) and (uffBackgroundColor in AFormatRecord^.UsedFormattingFields) then
begin
dw2 := dw2 or DWORD(MASK_XF_FILL_PATT_SOLID shl 26);
rec.BkGr3 := FixColor(AFormatRecord^.BackgroundColor);
end;
rec.Border_BkGr1 := DWordToLE(dw1);
rec.Border_BkGr2 := DWordToLE(dw2);
rec.BkGr3 := WordToLE(rec.BkGr3);
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
(*
procedure TsSpreadBIFF8Writer.WriteXF(AStream: TStream; AFontIndex: Word;
AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders;
const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault;
@@ -1222,47 +1379,7 @@ begin
AStream.WriteWord(WordToLE(ABackgroundColor))
else
AStream.WriteWord(0);
end;
procedure TsSpreadBIFF8Writer.WriteXFRecords(AStream: TStream);
begin
// XF0
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF1
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF2
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF3
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF4
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF5
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF6
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF7
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF8
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF9
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF10
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF11
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF12
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF13
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF14
WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// XF15 - Default, no formatting
WriteXF(AStream, 0, 0, 0, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES);
// Add all further non-standard/built-in formatting styles
ListAllFormattingStyles;
WriteXFFieldsForFormattingStyles(AStream);
end;
end;*)
{ TsSpreadBIFF8Reader }
@@ -1854,13 +1971,13 @@ var
ACol,ARow: Cardinal;
XF: WORD;
SSTIndex: DWORD;
rec: TBIFF8LabelSSTRecord;
rec: TBIFF8_LabelSSTRecord;
cell: PCell;
begin
rec.Row := 0; // to silence the compiler...
{ Read entire record, starting at Row }
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF8LabelSSTRecord) - 2*SizeOf(Word));
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF8_LabelSSTRecord) - 2*SizeOf(Word));
ARow := WordLEToN(rec.Row);
ACol := WordLEToN(rec.Col);
XF := WordLEToN(rec.XFIndex);
@@ -1922,7 +2039,7 @@ procedure TsSpreadBIFF8Reader.ReadXF(const AStream: TStream);
else Result := lsDashed;
end;
end;
(*
type
TXFRecord = packed record // see p. 224
FontIndex: Word; // Offset 0, Size 2
@@ -1936,120 +2053,147 @@ type
Border_Background_2: DWord; // Offset 14, Size 4
Border_Background_3: DWord; // Offset 18, Size 2
end;
*)
var
lData: TXFListData;
xf: TXFRecord;
rec: TBIFF8_XFRecord;
fmt: TsCellFormat;
// xf: TXFRecord;
b: Byte;
dw: DWord;
fill: Integer;
nfidx: Integer;
nfdata: TsNumFormatData;
i: Integer;
begin
xf.FontIndex := 0; // to silence the compiler...
InitFormatRecord(fmt);
fmt.ID := FCellFormatList.Count;
rec.FontIndex := 0; // to silence the compiler...
// Read entire xf record into a buffer
AStream.ReadBuffer(xf, SizeOf(xf));
lData := TXFListData.Create;
AStream.ReadBuffer(rec.FontIndex, SizeOf(rec) - 2*SizeOf(word));
// Font index
lData.FontIndex := WordLEToN(xf.FontIndex);
fmt.FontIndex := WordLEToN(rec.FontIndex);
if fmt.FontIndex = 1 then
Include(fmt.UsedFormattingFields, uffBold)
else if fmt.FontIndex > 1 then
Include(fmt.UsedFormattingFields, uffFont);
// Format index
lData.FormatIndex := WordLEToN(xf.FormatIndex);
// Number format index
nfidx := WordLEToN(rec.NumFormatIndex);
i := NumFormatList.FindByIndex(nfidx);
if i > -1 then begin
nfdata := NumFormatList.Items[i];
fmt.NumberFormat := nfdata.NumFormat;
fmt.NumberFormatStr := nfdata.FormatString;
if nfdata.NumFormat <> nfGeneral then
Include(fmt.UsedFormattingFields, uffNumberFormat);
end;
// Horizontal text alignment
b := xf.Align_TextBreak AND MASK_XF_HOR_ALIGN;
b := rec.Align_TextBreak AND MASK_XF_HOR_ALIGN;
if (b <= ord(High(TsHorAlignment))) then
lData.HorAlignment := TsHorAlignment(b)
else
lData.HorAlignment := haDefault;
begin
fmt.HorAlignment := TsHorAlignment(b);
if fmt.HorAlignment <> haDefault then
Include(fmt.UsedFormattingFields, uffHorAlign);
end;
// Vertical text alignment
b := (xf.Align_TextBreak AND MASK_XF_VERT_ALIGN) shr 4;
b := (rec.Align_TextBreak AND MASK_XF_VERT_ALIGN) shr 4;
if (b + 1 <= ord(high(TsVertAlignment))) then
begin
lData.VertAlignment := tsVertAlignment(b + 1); // + 1 due to vaDefault
fmt.VertAlignment := TsVertAlignment(b + 1); // + 1 due to vaDefault
// Unfortunately BIFF does not provide a "default" vertical alignment code.
// Without the following correction "non-formatted" cells would always have
// the uffVertAlign FormattingField set which contradicts the statement of
// not being formatted.
if lData.VertAlignment = vaBottom then
lData.VertAlignment := vaDefault;
end
else
lData.VertAlignment := vaDefault;
if fmt.VertAlignment = vaBottom then
fmt.VertAlignment := vaDefault;
if fmt.VertAlignment <> vaDefault then
Include(fmt.UsedFormattingFields, uffVertAlign);
end;
// Word wrap
lData.WordWrap := (xf.Align_TextBreak and MASK_XF_TEXTWRAP) <> 0;
if (rec.Align_TextBreak and MASK_XF_TEXTWRAP) <> 0 then
Include(fmt.UsedFormattingFields, uffWordwrap);
// TextRotation
case xf.XFRotation of
XF_ROTATION_HORIZONTAL : lData.TextRotation := trHorizontal;
XF_ROTATION_90DEG_CCW : ldata.TextRotation := rt90DegreeCounterClockwiseRotation;
XF_ROTATION_90DEG_CW : lData.TextRotation := rt90DegreeClockwiseRotation;
XF_ROTATION_STACKED : lData.TextRotation := rtStacked;
case rec.TextRotation of
XF_ROTATION_HORIZONTAL : fmt.TextRotation := trHorizontal;
XF_ROTATION_90DEG_CCW : fmt.TextRotation := rt90DegreeCounterClockwiseRotation;
XF_ROTATION_90DEG_CW : fmt.TextRotation := rt90DegreeClockwiseRotation;
XF_ROTATION_STACKED : fmt.TextRotation := rtStacked;
end;
if fmt.TextRotation <> trHorizontal then
Include(fmt.UsedFormattingFields, uffTextRotation);
// Cell borders
xf.Border_Background_1 := DWordLEToN(xf.Border_Background_1);
lData.Borders := [];
lData.BorderStyles := DEFAULT_BORDERSTYLES;
rec.Border_BkGr1 := DWordLEToN(rec.Border_BkGr1);
rec.Border_BkGr2 := DWordLEToN(rec.Border_BkGr2);
// the 4 masked bits encode the line style of the border line. 0 = no line
dw := xf.Border_Background_1 and MASK_XF_BORDER_LEFT;
dw := rec.Border_BkGr1 and MASK_XF_BORDER_LEFT;
if dw <> 0 then
begin
Include(lData.Borders, cbWest);
lData.BorderStyles[cbWest].LineStyle := FixLineStyle(dw);
Include(fmt.Border, cbWest);
fmt.BorderStyles[cbWest].LineStyle := FixLineStyle(dw);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_1 and MASK_XF_BORDER_RIGHT;
dw := rec.Border_BkGr1 and MASK_XF_BORDER_RIGHT;
if dw <> 0 then
begin
Include(lData.Borders, cbEast);
lData.BorderStyles[cbEast].LineStyle := FixLineStyle(dw shr 4);
Include(fmt.Border, cbEast);
fmt.BorderStyles[cbEast].LineStyle := FixLineStyle(dw shr 4);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_1 and MASK_XF_BORDER_TOP;
dw := rec.Border_BkGr1 and MASK_XF_BORDER_TOP;
if dw <> 0 then
begin
Include(lData.Borders, cbNorth);
lData.BorderStyles[cbNorth].LineStyle := FixLineStyle(dw shr 8);
Include(fmt.Border, cbNorth);
fmt.BorderStyles[cbNorth].LineStyle := FixLineStyle(dw shr 8);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM;
dw := rec.Border_BkGr1 and MASK_XF_BORDER_BOTTOM;
if dw <> 0 then
begin
Include(lData.Borders, cbSouth);
lData.BorderStyles[cbSouth].LineStyle := FixLineStyle(dw shr 12);
Include(fmt.Border, cbSouth);
fmt.BorderStyles[cbSouth].LineStyle := FixLineStyle(dw shr 12);
Include(fmt.UsedFormattingFields, uffBorder);
end;
dw := xf.Border_Background_2 and MASK_XF_BORDER_DIAGONAL;
dw := rec.Border_BkGr2 and MASK_XF_BORDER_DIAGONAL;
if dw <> 0 then
begin
lData.BorderStyles[cbDiagUp].LineStyle := FixLineStyle(dw shr 21);
lData.BorderStyles[cbDiagDown].LineStyle := lData.BorderStyles[cbDiagUp].LineStyle;
if xf.Border_Background_1 and MASK_XF_BORDER_SHOW_DIAGONAL_UP <> 0 then
Include(lData.Borders, cbDiagUp);
if xf.Border_Background_1 and MASK_XF_BORDER_SHOW_DIAGONAL_DOWN <> 0 then
Include(lData.Borders, cbDiagDown);
fmt.BorderStyles[cbDiagUp].LineStyle := FixLineStyle(dw shr 21);
fmt.BorderStyles[cbDiagDown].LineStyle := fmt.BorderStyles[cbDiagUp].LineStyle;
if rec.Border_BkGr1 and MASK_XF_BORDER_SHOW_DIAGONAL_UP <> 0 then
Include(fmt.Border, cbDiagUp);
if rec.Border_BkGr1 and MASK_XF_BORDER_SHOW_DIAGONAL_DOWN <> 0 then
Include(fmt.Border, cbDiagDown);
Include(fmt.UsedFormattingFields, uffBorder);
end;
// Border line colors
lData.BorderStyles[cbWest].Color := (xf.Border_Background_1 and MASK_XF_BORDER_LEFT_COLOR) shr 16;
lData.BorderStyles[cbEast].Color := (xf.Border_Background_1 and MASK_XF_BORDER_RIGHT_COLOR) shr 23;
lData.BorderStyles[cbNorth].Color := (xf.Border_Background_2 and MASK_XF_BORDER_TOP_COLOR);
lData.BorderStyles[cbSouth].Color := (xf.Border_Background_2 and MASK_XF_BORDER_BOTTOM_COLOR) shr 7;
lData.BorderStyles[cbDiagUp].Color := (xf.Border_Background_2 and MASK_XF_BORDER_DIAGONAL_COLOR) shr 14;
lData.BorderStyles[cbDiagDown].Color := lData.BorderStyles[cbDiagUp].Color;
fmt.BorderStyles[cbWest].Color := (rec.Border_BkGr1 and MASK_XF_BORDER_LEFT_COLOR) shr 16;
fmt.BorderStyles[cbEast].Color := (rec.Border_BkGr1 and MASK_XF_BORDER_RIGHT_COLOR) shr 23;
fmt.BorderStyles[cbNorth].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_TOP_COLOR);
fmt.BorderStyles[cbSouth].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_BOTTOM_COLOR) shr 7;
fmt.BorderStyles[cbDiagUp].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_DIAGONAL_COLOR) shr 14;
fmt.BorderStyles[cbDiagDown].Color := fmt.BorderStyles[cbDiagUp].Color;
// Background fill pattern
fill := (xf.Border_Background_2 and MASK_XF_BACKGROUND_PATTERN) shr 26;
fill := (rec.Border_BkGr2 and MASK_XF_BACKGROUND_PATTERN) shr 26;
// Background color
xf.Border_Background_3 := DWordLEToN(xf.Border_Background_3);
if fill <> 0 then
lData.BackgroundColor := xf.Border_Background_3 and $007F
else
lData.BackgroundColor := scTransparent; // this means "no fill"
rec.BkGr3 := DWordLEToN(rec.BkGr3);
if fill <> 0 then begin
fmt.BackgroundColor := rec.BkGr3 and $007F;
Include(fmt.UsedFormattingFields, uffBackgroundColor);
end else
fmt.BackgroundColor := scTransparent; // this means "no fill"
// Add the XF to the list
FXFList.Add(lData);
FCellFormatList.Add(fmt);
end;
procedure TsSpreadBIFF8Reader.ReadFont(const AStream: TStream);
@@ -2111,7 +2255,7 @@ begin
FWorkbook.AddFont(font);
end;
// Read the FORMAT record for formatting numerical data
// Read the (number) FORMAT record for formatting numerical data
procedure TsSpreadBIFF8Reader.ReadFormat(AStream: TStream);
var
fmtString: String;

View File

@@ -159,6 +159,10 @@ const
MASK_XF_VERT_ALIGN_BOTTOM = $20;
MASK_XF_VERT_ALIGN_JUSTIFIED = $30;
{ XF FILL PATTERNS }
MASK_XF_FILL_PATT_EMPTY = $00;
MASK_XF_FILL_PATT_SOLID = $01;
{ Cell Addresses constants, valid for BIFF2-BIFF5 }
MASK_EXCEL_ROW = $3FFF;
MASK_EXCEL_RELATIVE_COL = $4000;
@@ -180,6 +184,9 @@ const
ERR_OVERFLOW = $24; // #NUM!
ERR_ARG_ERROR = $2A; // #N/A (not enough, or too many, arguments)
{ Index of last built-in XF format record }
LAST_BUILTIN_XF = 15;
type
TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28
@@ -196,7 +203,7 @@ type
// Converts an fps error value to the byte code needed in xls files
function ConvertToExcelError(AValue: TsErrorValue): byte;
type
type (*
{ Contents of the XF record to be stored in the XFList of the reader }
TXFListData = class
public
@@ -209,7 +216,7 @@ type
Borders: TsCellBorders;
BorderStyles: TsCellBorderStyles;
BackgroundColor: TsColor;
end;
end; *)
{ TsBIFFNumFormatList }
TsBIFFNumFormatList = class(TsCustomNumFormatList)
@@ -227,21 +234,21 @@ type
FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding
FDateMode: TDateMode;
FPaletteFound: Boolean;
FXFList: TFPList; // of TXFListData
// FXFList: TFPList; // of TXFListData
FIncompleteCell: PCell;
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); overload;
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual; overload;
// procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); overload;
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual; //overload;
procedure CreateNumFormatList; override;
// Extracts a number out of an RK value
function DecodeRKValue(const ARK: DWORD): Double;
// Returns the numberformat for a given XF record
procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; //out ADecimals: Byte;
//out ACurrencySymbol: String;
out ANumberFormatStr: String); virtual;
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual;
{
// Finds format record for XF record pointed to by cell
// Will not return info for built-in formats
function FindNumFormatDataForCell(const AXFIndex: Integer): TsNumFormatData;
}
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
ANumberFormatStr: String; out ADateTime: TDateTime): Boolean;
@@ -303,7 +310,7 @@ type
public
constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
// destructor Destroy; override;
end;
{ TsSpreadBIFFWriter }
@@ -313,9 +320,9 @@ type
FDateMode: TDateMode;
FLastRow: Cardinal;
FLastCol: Cardinal;
procedure AddDefaultFormats; override;
// procedure AddDefaultFormats; override;
procedure CreateNumFormatList; override;
function FindXFIndex(ACell: PCell): Integer;
function FindXFIndex(ACell: PCell): Integer; virtual;
function FixColor(AColor: TsColor): TsColor; override;
procedure GetLastRowCallback(ACell: PCell; AStream: TStream);
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
@@ -343,14 +350,14 @@ type
// Writes out ERROR cell record
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TsErrorValue; ACell: PCell); override;
// Writes out a FORMAT record
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); virtual;
// Writes out all FORMAT records
procedure WriteFormats(AStream: TStream);
// Writes out a FORMULA record; formula is stored in cell already
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override;
// Writes out a FORMAT record
procedure WriteNumFormat(AStream: TStream; ANumFormatData: TsNumFormatData;
AListIndex: Integer); virtual;
// Writes out all FORMAT records
procedure WriteNumFormats(AStream: TStream);
// Writes out a floating point NUMBER record
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Double; ACell: PCell); override;
@@ -381,6 +388,7 @@ type
procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell;
const AFormula: TsRPNFormula; UseRelAddr: Boolean; var RPNLength: Word);
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
// Writes out a SELECTION record
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet);
@@ -395,7 +403,13 @@ type
// Writes out a WINDOW1 record
procedure WriteWindow1(AStream: TStream); virtual;
// Writes the index of the XF record used in the given cell
procedure WriteXFIndex(AStream: TStream; ACell: PCell);
//procedure WriteXFIndex(AStream: TStream; ACell: PCell);
// Writes an XF record
procedure WriteXF(AStream: TStream; ACellFormat: PsCellFormat;
XFType_Prot: Byte = 0); virtual;
// Writes all xF records
procedure WriteXFRecords(AStream: TStream);
public
constructor Create(AWorkbook: TsWorkbook); override;
@@ -562,47 +576,47 @@ begin
fs := Workbook.FormatSettings;
cs := Workbook.FormatSettings.CurrencyString;
AddFormat( 0, '', nfGeneral);
AddFormat( 1, '0', nfFixed);
AddFormat( 2, '0.00', nfFixed);
AddFormat( 3, '#,##0', nfFixedTh);
AddFormat( 4, '#,##0.00', nfFixedTh);
AddFormat( 5, '"'+cs+'"#,##0;("'+cs+'"#,##0)', nfCurrency);
AddFormat( 6, '"'+cs+'"#,##0;[Red]("'+cs+'"#,##0)', nfCurrencyRed);
AddFormat( 7, '"'+cs+'"#,##0.00;("'+cs+'"#,##0.00)', nfCurrency);
AddFormat( 8, '"'+cs+'"#,##0.00;[Red]("'+cs+'"#,##0.00)', nfCurrencyRed);
AddFormat( 9, '0%', nfPercentage);
AddFormat(10, '0.00%', nfPercentage);
AddFormat(11, '0.00E+00', nfExp);
AddFormat( 0, nfGeneral, '');
AddFormat( 1, nfFixed, '0');
AddFormat( 2, nfFixed, '0.00');
AddFormat( 3, nfFixedTh, '#,##0');
AddFormat( 4, nfFixedTh, '#,##0.00');
AddFormat( 5, nfCurrency, '"'+cs+'"#,##0;("'+cs+'"#,##0)');
AddFormat( 6, nfCurrencyRed, '"'+cs+'"#,##0;[Red]("'+cs+'"#,##0)');
AddFormat( 7, nfCurrency, '"'+cs+'"#,##0.00;("'+cs+'"#,##0.00)');
AddFormat( 8, nfCurrencyRed, '"'+cs+'"#,##0.00;[Red]("'+cs+'"#,##0.00)');
AddFormat( 9, nfPercentage, '0%');
AddFormat(10, nfPercentage, '0.00%');
AddFormat(11, nfExp, '0.00E+00');
// fraction formats 12 ('# ?/?') and 13 ('# ??/??') not supported
AddFormat(14, fs.ShortDateFormat, nfShortDate); // 'M/D/YY'
AddFormat(15, fs.LongDateFormat, nfLongDate); // 'D-MMM-YY'
AddFormat(16, 'd/mmm', nfCustom); // 'D-MMM'
AddFormat(17, 'mmm/yy', nfCustom); // 'MMM-YY'
AddFormat(18, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM); // 'h:mm AM/PM'
AddFormat(19, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM); // 'h:mm:ss AM/PM'
AddFormat(20, fs.ShortTimeFormat, nfShortTime); // 'h:mm'
AddFormat(21, fs.LongTimeFormat, nfLongTime); // 'h:mm:ss'
AddFormat(22, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime); // 'M/D/YY h:mm' (localized)
AddFormat(14, nfShortDate, fs.ShortDateFormat); // 'M/D/YY'
AddFormat(15, nfLongDate, fs.LongDateFormat); // 'D-MMM-YY'
AddFormat(16, nfCustom, 'd/mmm'); // 'D-MMM'
AddFormat(17, nfCustom, 'mmm/yy'); // 'MMM-YY'
AddFormat(18, nfShortTimeAM, AddAMPM(fs.ShortTimeFormat, fs)); // 'h:mm AM/PM'
AddFormat(19, nfLongTimeAM, AddAMPM(fs.LongTimeFormat, fs)); // 'h:mm:ss AM/PM'
AddFormat(20, nfShortTime, fs.ShortTimeFormat); // 'h:mm'
AddFormat(21, nfLongTime, fs.LongTimeFormat); // 'h:mm:ss'
AddFormat(22, nfShortDateTime, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat); // 'M/D/YY h:mm' (localized)
// 23..36 not supported
AddFormat(37, '_(#,##0_);(#,##0)', nfCurrency);
AddFormat(38, '_(#,##0_);[Red](#,##0)', nfCurrencyRed);
AddFormat(39, '_(#,##0.00_);(#,##0.00)', nfCurrency);
AddFormat(40, '_(#,##0.00_);[Red](#,##0.00)', nfCurrencyRed);
AddFormat(41, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)', nfCustom);
AddFormat(42, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', nfCustom);
AddFormat(43, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)', nfCustom);
AddFormat(44, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', nfCustom);
AddFormat(45, 'nn:ss', nfCustom);
AddFormat(46, '[h]:nn:ss', nfTimeInterval);
AddFormat(47, 'nn:ss.z', nfCustom);
AddFormat(48, '##0.0E+00', nfCustom);
AddFormat(37, nfCurrency, '_(#,##0_);(#,##0)');
AddFormat(38, nfCurrencyRed, '_(#,##0_);[Red](#,##0)');
AddFormat(39, nfCurrency, '_(#,##0.00_);(#,##0.00)');
AddFormat(40, nfCurrencyRed, '_(#,##0.00_);[Red](#,##0.00)');
AddFormat(41, nfCustom, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)');
AddFormat(42, nfCustom, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)');
AddFormat(43, nfCustom, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)');
AddFormat(44, nfCustom, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)');
AddFormat(45, nfCustom, 'nn:ss');
AddFormat(46, nfTimeInterval, '[h]:nn:ss');
AddFormat(47, nfCustom, 'nn:ss.z');
AddFormat(48, nfCustom, '##0.0E+00');
// 49 ("Text") not supported
// All indexes from 0 to 163 are reserved for built-in formats.
// The first user-defined format starts at 164.
FFirstFormatIndexInFile := 164;
FNextFormatIndex := 164;
FFirstNumFormatIndexInFile := 164;
FNextNumFormatIndex := 164;
end;
procedure TsBIFFNumFormatList.ConvertBeforeWriting(var AFormatString: String;
@@ -628,7 +642,8 @@ end;
constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
FXFList := TFPList.Create;
FCellFormatList := TsCellFormatList.Create(true);
// Allow duplicates! XF indexes get out of sync if not all format records are in list
// Initial base date in case it won't be read from file
FDateMode := dm1900;
// Limitations of BIFF5 and BIFF8 file format
@@ -637,6 +652,7 @@ begin
FLimitations.MaxPaletteSize := 64;
end;
{
destructor TsSpreadBIFFReader.Destroy;
var
j: integer;
@@ -645,7 +661,8 @@ begin
FXFList.Free;
inherited Destroy;
end;
}
(*
{ Applies the XF formatting referred to by XFIndex to the specified cell }
procedure TsSpreadBIFFReader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Word);
@@ -655,15 +672,23 @@ begin
lCell := FWorksheet.GetCell(ARow, ACol);
ApplyCellFormatting(lCell, XFIndex);
end;
*)
{ Applies the XF formatting referred to by XFIndex to the specified cell }
procedure TsSpreadBIFFReader.ApplyCellFormatting(ACell: PCell; XFIndex: Word);
var
XFData: TXFListData;
fmt: PsCellFormat;
i: Integer;
begin
if Assigned(ACell) then begin
XFData := TXFListData(FXFList.Items[XFIndex]);
i := FCellFormatList.FindIndexOfID(XFIndex);
if i > -1 then
begin
fmt := FCellFormatList.Items[i];
ACell^.FormatIndex := FWorkbook.AddCellFormat(fmt^);
end else
ACell^.FormatIndex := 0;
(*
// Font
if XFData.FontIndex = 1 then
Include(ACell^.UsedFormattingFields, uffBold)
@@ -712,6 +737,7 @@ begin
ACell^.BackgroundColor := XFData.BackgroundColor;
end else
Exclude(ACell^.UsedFormattingFields, uffBackgroundColor);
*)
end;
end;
@@ -758,9 +784,24 @@ end;
{ Extracts number format data from an XF record index by AXFIndex.
Valid for BIFF5-BIFF8. Needs to be overridden for BIFF2 }
procedure TsSpreadBIFFReader.ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; //out ADecimals: Byte;
//out ACurrencySymbol: String;
out ANumberFormatStr: String);
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String);
var
fmt: PsCellFormat;
i: Integer;
begin
i := FCellFormatList.FindIndexOfID(AXFIndex);
if i > -1 then
begin
fmt := FCellFormatList.Items[i];
ANumberFormat := fmt^.NumberFormat;
ANumberFormatStr := fmt^.NumberFormatStr;
end else
begin
ANumberFormat := nfGeneral;
ANumberFormatStr := '';
end;
end;
{
var
lNumFormatData: TsNumFormatData;
begin
@@ -772,22 +813,24 @@ begin
ANumberFormat := nfGeneral;
ANumberFormatStr := '';
end;
end;
end; }
(*
{ Determines the format data (for numerical formatting) which belong to a given
XF record. }
function TsSpreadBIFFReader.FindNumFormatDataForCell(const AXFIndex: Integer
): TsNumFormatData;
var
lXFData: TXFListData;
fmt: TsCellFormat;
i: Integer;
begin
Result := nil;
fmt := FFormatList.FindByID(AXFIndex);
i := NumFormatList.FindByIndex(
lXFData := TXFListData(FXFList.Items[AXFIndex]);
i := NumFormatList.FindByIndex(lXFData.FormatIndex);
if i <> -1 then Result := NumFormatList[i];
end;
*)
{ Convert the number to a date/time and return that if it is }
function TsSpreadBIFFReader.IsDateTime(Number: Double;
ANumberFormat: TsNumberFormat; ANumberFormatStr: String;
@@ -1010,7 +1053,7 @@ begin
FWorksheet.DefaultRowHeight := h - ROW_HEIGHT_CORRECTION;
end;
// Read the FORMAT record for formatting numerical data
// Read the (number) FORMAT record for formatting numerical data
procedure TsSpreadBIFFReader.ReadFormat(AStream: TStream);
begin
Unused(AStream);
@@ -1774,7 +1817,7 @@ destructor TsSpreadBIFFWriter.Destroy;
begin
inherited Destroy;
end;
(*
{ These are default style formats which are added as XF fields regardless of
being used in the document or not.
Currently, only one additional default format is supported ("bold").
@@ -1795,6 +1838,7 @@ begin
NextXFIndex := 15 + Length(FFormattingStyles);
// "15" is the index of the last pre-defined xf record
end;
*)
{ Creates the correct version of the number format list. It is for BIFF file
formats.
@@ -1807,6 +1851,10 @@ end;
{ Determines the index of the XF record, according to formatting of the given cell }
function TsSpreadBIFFWriter.FindXFIndex(ACell: PCell): Integer;
begin
Result := LAST_BUILTIN_XF + ACell^.FormatIndex;
end;
{
var
idx: Integer;
cell: TCell;
@@ -1829,7 +1877,7 @@ begin
else
Result := FFormattingStyles[idx].Row;
end;
}
function TsSpreadBIFFWriter.FixColor(AColor: TsColor): TsColor;
var
rgb: TsColorValue;
@@ -2061,30 +2109,31 @@ begin
end;
{ Writes a BIFF format record defined in AFormatData. AListIndex the index of
the formatdata in the format list (not the FormatIndex!).
{ Writes a BIFF number format record defined in AFormatData.
AListIndex the index of the numformatdata in the numformat list
(not the FormatIndex!).
Needs to be overridden by descendants. }
procedure TsSpreadBIFFWriter.WriteFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer);
procedure TsSpreadBIFFWriter.WriteNumFormat(AStream: TStream;
ANumFormatData: TsNumFormatData; AListIndex: Integer);
begin
Unused(AStream, AFormatData, AListIndex);
Unused(AStream, ANumFormatData, AListIndex);
// needs to be overridden
end;
{ Writes all number formats to the stream. Saving starts at the item with the
FirstFormatIndexInFile. }
procedure TsSpreadBIFFWriter.WriteFormats(AStream: TStream);
procedure TsSpreadBIFFWriter.WriteNumFormats(AStream: TStream);
var
i: Integer;
item: TsNumFormatData;
begin
ListAllNumFormats;
i := NumFormatList.FindByIndex(NumFormatList.FirstFormatIndexInFile);
i := NumFormatList.FindByIndex(NumFormatList.FirstNumFormatIndexInFile);
if i > -1 then
while i < NumFormatList.Count do begin
item := NumFormatList[i];
if item <> nil then begin
WriteFormat(AStream, item, i);
WriteNumFormat(AStream, item, i);
end;
inc(i);
end;
@@ -2363,8 +2412,7 @@ begin
AStream.WriteWord(WordToLE(ACol));
{ Index to XF record, according to formatting }
//AStream.WriteWord(0);
WriteXFIndex(AStream, ACell);
AStream.WriteWord(FindXFIndex(ACell));
{ Encoded result of RPN formula }
WriteRPNResult(AStream, ACell);
@@ -2654,6 +2702,7 @@ var
colindex: Cardinal;
rowheight: Word;
h: Single;
fmt: PsCellFormat;
begin
if (ARowIndex >= FLimitations.MaxRowCount) or
(AFirstColIndex >= FLimitations.MaxColCount) or
@@ -2665,14 +2714,28 @@ begin
spaceabove := false;
spacebelow := false;
colindex := AFirstColIndex;
while colindex <= ALastColIndex do begin
while colindex <= ALastColIndex do
begin
cell := ASheet.FindCell(ARowindex, colindex);
if (cell <> nil) then
begin
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if (uffBorder in fmt^.UsedFormattingFields) then
begin
if (cbNorth in fmt^.Border) and (fmt^.BorderStyles[cbNorth].LineStyle = lsThick)
then spaceabove := true;
if (cbSouth in fmt^.Border) and (fmt^.BorderStyles[cbSouth].LineStyle = lsThick)
then spacebelow := true;
end;
end;
{
if (cell <> nil) and (uffBorder in cell^.UsedFormattingFields) then begin
if (cbNorth in cell^.Border) and (cell^.BorderStyles[cbNorth].LineStyle = lsThick)
then spaceabove := true;
if (cbSouth in cell^.Border) and (cell^.BorderStyles[cbSouth].LineStyle = lsThick)
then spacebelow := true;
end;
}
if spaceabove and spacebelow then break;
inc(colindex);
end;
@@ -3023,10 +3086,63 @@ begin
AStream.WriteWord(WordToLE(600));
end;
procedure TsSpreadBIFFWriter.WriteXF(AStream: TStream; ACellFormat: PsCellFormat;
XFType_Prot: Byte = 0);
begin
Unused(AStream, ACellFormat, XFType_Prot);
end;
procedure TsSpreadBIFFWriter.WriteXFRecords(AStream: TStream);
var
i: Integer;
begin
// XF0
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF1
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF2
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF3
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF4
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF5
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF6
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF7
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF8
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF9
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF10
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF11
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF12
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF13
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF14
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF15 - Default, no formatting
WriteXF(AStream, nil, 0);
// Add all further non-standard format records
// The first style was already added --> begin loop with 1
for i:=1 to Workbook.GetNumCellFormats - 1 do
WriteXF(AStream, Workbook.GetPointerToCellFormat(i), 0);
end;
(*
{ Write the index of the XF record, according to formatting of the given cell
Valid for BIFF5 and BIFF8.
BIFF2 is handled differently. }
procedure TsSpreadBIFFWriter.WriteXFIndex(AStream: TStream; ACell: PCell);
begin
AStream.WriteWord(AStream, LAST_BUILTIN_XF + ACell^.FormatIndex);
{
var
lIndex: Integer;
lXFIndex: Word;
@@ -3051,7 +3167,8 @@ begin
lXFIndex := FFormattingStyles[lIndex].Row;
AStream.WriteWord(WordToLE(lXFIndex));
end;
}
end; *)
end.

View File

@@ -58,7 +58,7 @@ const
{ Function Tokens }
// _R: reference; _V: value; _A: array
// Offset 0: token; offset 1: index to a built-in sheet function ( ➜ 3.111)
// Offset 0: token; offset 1: index to a built-in sheet function ( ➜ 3.111) )
INT_EXCEL_TOKEN_FUNC_R = $21;
INT_EXCEL_TOKEN_FUNC_V = $41;
INT_EXCEL_TOKEN_FUNC_A = $61;
@@ -69,7 +69,7 @@ const
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
{ Special tokens }
INT_EXCEL_TOKEN_TEXP = $01; // cell belongs to shared formula
INT_EXCEL_TOKEN_TEXP = $01; // cell belongs to shared formula
{ Built-in/worksheet functions }
INT_EXCEL_SHEET_FUNC_COUNT = 0;

View File

@@ -22,8 +22,9 @@ http://openxmldeveloper.org/default.aspx
also:
http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx#BMworksheetworkbook
AUTHORS: Felipe Monteiro de Carvalho
AUTHORS: Felipe Monteiro de Carvalho, Reinier Olislagers, Werner Pamler
}
unit xlsxooxml;
{$ifdef fpc}
@@ -61,7 +62,6 @@ type
FDateMode: TDateMode;
FPointSeparatorSettings: TFormatSettings;
FSharedStrings: TStringList;
FXfList: TFPList;
FFillList: TFPList;
FBorderList: TFPList;
FThemeColors: array of TsColorValue;
@@ -105,16 +105,23 @@ type
FDateMode: TDateMode;
FPointSeparatorSettings: TFormatSettings;
FSharedStringsCount: Integer;
FFillList: array of PCell;
FFillList: array of PsCellFormat;
FBorderList: array of PsCellFormat;
{
FFillList: array of TsCell;
FBorderList: array of PCell;
}
protected
{ Helper routines }
procedure AddDefaultFormats; override;
// procedure AddDefaultFormats; override;
procedure CreateNumFormatList; override;
procedure CreateStreams;
procedure DestroyStreams;
{
function FindBorderInList(ACell: PCell): Integer;
function FindFillInList(ACell: PCell): Integer;
function FindFillInList(ACell: PCell): Integer; }
function FindBorderInList(AFormat: PsCellFormat): Integer;
function FindFillInList(AFormat: PsCellFormat): Integer;
function GetStyleIndex(ACell: PCell): Cardinal;
procedure ListAllBorders;
procedure ListAllFills;
@@ -305,7 +312,7 @@ type
Borders: TsCellBorders;
BorderStyles: TsCellBorderStyles;
end;
(*
TXFListData = class
NumFmtIndex: Integer;
FontIndex: Integer;
@@ -316,7 +323,7 @@ type
WordWrap: Boolean;
TextRotation: TsTextRotation;
end;
*)
{ TsOOXMLNumFormatList }
@@ -332,47 +339,47 @@ begin
fs := Workbook.FormatSettings;
cs := AnsiToUTF8(Workbook.FormatSettings.CurrencyString);
AddFormat( 0, '', nfGeneral);
AddFormat( 1, '0', nfFixed);
AddFormat( 2, '0.00', nfFixed);
AddFormat( 3, '#,##0', nfFixedTh);
AddFormat( 4, '#,##0.00', nfFixedTh);
AddFormat( 5, '"'+cs+'"#,##0_);("'+cs+'"#,##0)', nfCurrency);
AddFormat( 6, '"'+cs+'"#,##0_);[Red]("'+cs+'"#,##0)', nfCurrencyRed);
AddFormat( 7, '"'+cs+'"#,##0.00_);("'+cs+'"#,##0.00)', nfCurrency);
AddFormat( 8, '"'+cs+'"#,##0.00_);[Red]("'+cs+'"#,##0.00)', nfCurrencyRed);
AddFormat( 9, '0%', nfPercentage);
AddFormat(10, '0.00%', nfPercentage);
AddFormat(11, '0.00E+00', nfExp);
AddFormat( 0, nfGeneral, '');
AddFormat( 1, nfFixed, '0');
AddFormat( 2, nfFixed, '0.00');
AddFormat( 3, nfFixedTh, '#,##0');
AddFormat( 4, nfFixedTh, '#,##0.00');
AddFormat( 5, nfCurrency, '"'+cs+'"#,##0_);("'+cs+'"#,##0)');
AddFormat( 6, nfCurrencyRed, '"'+cs+'"#,##0_);[Red]("'+cs+'"#,##0)');
AddFormat( 7, nfCurrency, '"'+cs+'"#,##0.00_);("'+cs+'"#,##0.00)');
AddFormat( 8, nfCurrencyRed, '"'+cs+'"#,##0.00_);[Red]("'+cs+'"#,##0.00)');
AddFormat( 9, nfPercentage, '0%');
AddFormat(10, nfPercentage, '0.00%');
AddFormat(11, nfExp, '0.00E+00');
// fraction formats 12 ('# ?/?') and 13 ('# ??/??') not supported
AddFormat(14, fs.ShortDateFormat, nfShortDate); // 'M/D/YY'
AddFormat(15, fs.LongDateFormat, nfLongDate); // 'D-MMM-YY'
AddFormat(16, 'd/mmm', nfCustom); // 'D-MMM'
AddFormat(17, 'mmm/yy', nfCustom); // 'MMM-YY'
AddFormat(18, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM); // 'h:mm AM/PM'
AddFormat(19, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM); // 'h:mm:ss AM/PM'
AddFormat(20, fs.ShortTimeFormat, nfShortTime); // 'h:mm'
AddFormat(21, fs.LongTimeFormat, nfLongTime); // 'h:mm:ss'
AddFormat(22, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime); // 'M/D/YY h:mm' (localized)
AddFormat(14, nfShortDate, fs.ShortDateFormat); // 'M/D/YY'
AddFormat(15, nfLongDate, fs.LongDateFormat); // 'D-MMM-YY'
AddFormat(16, nfCustom, 'd/mmm'); // 'D-MMM'
AddFormat(17, nfCustom, 'mmm/yy'); // 'MMM-YY'
AddFormat(18, nfShortTimeAM, AddAMPM(fs.ShortTimeFormat, fs)); // 'h:mm AM/PM'
AddFormat(19, nfLongTimeAM, AddAMPM(fs.LongTimeFormat, fs)); // 'h:mm:ss AM/PM'
AddFormat(20, nfShortTime, fs.ShortTimeFormat); // 'h:mm'
AddFormat(21, nfLongTime, fs.LongTimeFormat); // 'h:mm:ss'
AddFormat(22, nfShortDateTime, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat); // 'M/D/YY h:mm' (localized)
// 23..36 not supported
AddFormat(37, '_(#,##0_);(#,##0)', nfCurrency);
AddFormat(38, '_(#,##0_);[Red](#,##0)', nfCurrencyRed);
AddFormat(39, '_(#,##0.00_);(#,##0.00)', nfCurrency);
AddFormat(40, '_(#,##0.00_);[Red](#,##0.00)', nfCurrencyRed);
AddFormat(41, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)', nfCustom);
AddFormat(42, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', nfCustom);
AddFormat(43, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)', nfCustom);
AddFormat(44, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', nfCustom);
AddFormat(45, 'nn:ss', nfCustom);
AddFormat(46, '[h]:nn:ss', nfTimeInterval);
AddFormat(47, 'nn:ss.z', nfCustom);
AddFormat(48, '##0.0E+00', nfCustom);
AddFormat(37, nfCurrency, '_(#,##0_);(#,##0)');
AddFormat(38, nfCurrencyRed, '_(#,##0_);[Red](#,##0)');
AddFormat(39, nfCurrency, '_(#,##0.00_);(#,##0.00)');
AddFormat(40, nfCurrencyRed, '_(#,##0.00_);[Red](#,##0.00)');
AddFormat(41, nfCustom, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)');
AddFormat(42, nfCustom, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)');
AddFormat(43, nfCustom, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)');
AddFormat(44, nfCustom, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)');
AddFormat(45, nfCustom, 'nn:ss');
AddFormat(46, nfTimeInterval, '[h]:nn:ss');
AddFormat(47, nfCustom, 'nn:ss.z');
AddFormat(48, nfCustom, '##0.0E+00');
// 49 ("Text") not supported
// All indexes from 0 to 163 are reserved for built-in formats.
// The first user-defined format starts at 164.
FFirstFormatIndexInFile := 164;
FNextFormatIndex := 164;
FFirstNumFormatIndexInFile := 164;
FNextNumFormatIndex := 164;
end;
procedure TsOOXMLNumFormatList.ConvertBeforeWriting(var AFormatString: String;
@@ -407,7 +414,8 @@ begin
FSharedStrings := TStringList.Create;
FFillList := TFPList.Create;
FBorderList := TFPList.Create;
FXfList := TFPList.Create;
FCellFormatList := TsCellFormatList.Create(true);
// Allow duplicates because xf indexes used in cell records cannot be found any more.
FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.';
@@ -417,9 +425,6 @@ destructor TsSpreadOOXMLReader.Destroy;
var
j: Integer;
begin
for j := FXfList.Count-1 downto 0 do TObject(FXfList[j]).Free;
FXfList.Free;
for j := FFillList.Count-1 downto 0 do TObject(FFillList[j]).Free;
FFillList.Free;
@@ -432,14 +437,24 @@ begin
inherited Destroy;
end;
procedure TsSpreadOOXMLReader.ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
procedure TsSpreadOOXMLReader.ApplyCellFormatting(ACell: PCell; XFIndex: Integer);
var
i: Integer;
fmt: PsCellFormat;
{
xf: TXfListData;
numFmtData: TsNumFormatData;
fillData: TFillListData;
borderData: TBorderListData;
j: Integer;
}
begin
if Assigned(ACell) then begin
i := FCellFormatList.FindIndexOfID(XFIndex);
fmt := FCellFormatList.Items[i];
ACell^.FormatIndex := FWorkbook.AddCellFormat(fmt^);
end;
(*
if Assigned(ACell) then begin
xf := TXFListData(FXfList.Items[XfIndex]);
@@ -505,7 +520,7 @@ begin
end;
end;
end;
*)
end;
procedure TsSpreadOOXMLReader.CreateNumFormatList;
@@ -626,6 +641,7 @@ var
formulaStr: String;
sstIndex: Integer;
number: Double;
fmt: TsCellFormat;
begin
if ANode = nil then
exit;
@@ -644,8 +660,11 @@ begin
// get style index
s := GetAttrValue(ANode, 's');
if s <> '' then
if s <> '' then begin
ApplyCellFormatting(cell, StrToInt(s));
fmt := Workbook.GetCellFormat(cell^.FormatIndex);
end else
InitFormatRecord(fmt);
// get data
datanode := ANode.FirstChild;
@@ -699,10 +718,10 @@ begin
if (s = '') or (s = 'n') then begin
// Number or date/time, depending on format
number := StrToFloat(dataStr, FPointSeparatorSettings);
if IsDateTimeFormat(cell^.NumberFormatStr) then begin
if cell^.NumberFormat <> nfTimeInterval then // no correction of time origin for "time interval" format
if IsDateTimeFormat(fmt.NumberFormatStr) then begin
if fmt.NumberFormat <> nfTimeInterval then // no correction of time origin for "time interval" format
number := ConvertExcelDateTimeToDateTime(number, FDateMode);
AWorksheet.WriteDateTime(cell, number, cell^.NumberFormatStr)
AWorksheet.WriteDateTime(cell, number, fmt.NumberFormatStr)
end
else
AWorksheet.WriteNumber(cell, number);
@@ -751,79 +770,133 @@ var
node: TDOMNode;
childNode: TDOMNode;
nodeName: String;
xf: TXfListData;
fmt: TsCellFormat;
s1, s2: String;
i, numFmtIndex, fillIndex, borderIndex: Integer;
numFmtData: TsNumFormatData;
fillData: TFillListData;
borderData: TBorderListData;
begin
node := ANode.FirstChild;
while Assigned(node) do begin
while Assigned(node) do
begin
nodeName := node.NodeName;
if nodeName = 'xf' then begin
xf := TXfListData.Create;
if nodeName = 'xf' then
begin
InitFormatRecord(fmt);
fmt.ID := FCellFormatList.Count;
fmt.Name := '';
// strange: sometimes the "apply*" are missing. Therefore, it may be better
// to check against "<>0" instead of "=1"
s1 := GetAttrValue(node, 'numFmtId');
s2 := GetAttrValue(node, 'applyNumberFormat');
if (s1 <> '') and (s2 <> '0') then xf.NumFmtIndex := StrToInt(s1);
if (s1 <> '') and (s2 <> '0') then
begin
numFmtIndex := StrToInt(s1);
i := NumFormatList.FindByIndex(numFmtIndex);
if i > -1 then
begin
numFmtData := NumFormatList.Items[i];
fmt.NumberFormat := numFmtData.NumFormat;
fmt.NumberFormatStr := numFmtData.FormatString;
if numFmtData.NumFormat <> nfGeneral then
Include(fmt.UsedFormattingFields, uffNumberFormat);
end;
end;
s1 := GetAttrValue(node, 'fontId');
s2 := GetAttrValue(node, 'applyFont');
if (s1 <> '') and (s2 <> '0') then xf.FontIndex := StrToInt(s1);
if (s1 <> '') and (s2 <> '0') then
begin
fmt.FontIndex := StrToInt(s1);
if fmt.FontIndex = 1 then
Include(fmt.UsedFormattingFields, uffBold)
else if fmt.FontIndex > 1 then
Include(fmt.UsedFormattingFields, uffFont);
end;
s1 := GetAttrValue(node, 'fillId');
s2 := GetAttrValue(node, 'applyFill');
if (s1 <> '') and (s2 <> '0') then xf.FillIndex := StrToInt(s1);
if (s1 <> '') and (s2 <> '0') then
begin
fillIndex := StrToInt(s1);
fillData := FFillList[fillIndex];
if (fillData <> nil) and (fillData.PatternType <> 'none') then begin
Include(fmt.UsedFormattingFields, uffBackgroundColor);
fmt.BackgroundColor := fillData.FgColor;
end;
end;
s1 := GetAttrValue(node, 'borderId');
s2 := GetAttrValue(node, 'applyBorder');
if (s1 <> '') and (s2 <> '0') then xf.BorderIndex := StrToInt(s1);
if (s1 <> '') and (s2 <> '0') then
begin
borderIndex := StrToInt(s1);
borderData := FBorderList[borderIndex];
if (borderData <> nil) then
begin
fmt.BorderStyles := borderData.BorderStyles;
fmt.Border := borderData.Borders;
end;
end;
s2 := GetAttrValue(node, 'applyAlignment');
if (s2 <> '0') then begin
if (s2 <> '0') and (s2 <> '') then begin
childNode := node.FirstChild;
while Assigned(childNode) do begin
nodeName := childNode.NodeName;
if nodeName = 'alignment' then begin
s1 := GetAttrValue(childNode, 'horizontal');
if s1 = 'left' then
xf.HorAlignment := haLeft
fmt.HorAlignment := haLeft
else
if s1 = 'center' then
xf.HorAlignment := haCenter
fmt.HorAlignment := haCenter
else
if s1 = 'right' then
xf.HorAlignment := haRight;
fmt.HorAlignment := haRight;
s1 := GetAttrValue(childNode, 'vertical');
if s1 = 'top' then
xf.VertAlignment := vaTop
fmt.VertAlignment := vaTop
else
if s1 = 'center' then
xf.VertAlignment := vaCenter
fmt.VertAlignment := vaCenter
else
if s1 = 'bottom' then
xf.VertAlignment := vaBottom;
fmt.VertAlignment := vaBottom;
s1 := GetAttrValue(childNode, 'wrapText');
if (s1 <> '0') then
xf.WordWrap := true;
Include(fmt.UsedFormattingFields, uffWordWrap);
s1 := GetAttrValue(childNode, 'textRotation');
if s1 = '90' then
xf.TextRotation := rt90DegreeCounterClockwiseRotation
fmt.TextRotation := rt90DegreeCounterClockwiseRotation
else
if s1 = '180' then
xf.TextRotation := rt90DegreeClockwiseRotation
fmt.TextRotation := rt90DegreeClockwiseRotation
else
if s1 = '255' then
xf.TextRotation := rtStacked
fmt.TextRotation := rtStacked
else
xf.TextRotation := trHorizontal;
fmt.TextRotation := trHorizontal;
end;
childNode := childNode.NextSibling;
end;
end;
FXfList.Add(xf);
if fmt.FontIndex > 0 then
Include(fmt.UsedFormattingFields, uffFont);
if fmt.Border <> [] then
Include(fmt.UsedFormattingFields, uffBorder);
if fmt.HorAlignment <> haDefault then
Include(fmt.UsedFormattingFields, uffHorAlign);
if fmt.VertAlignment <> vaDefault then
Include(fmt.UsedFormattingFields, uffVertAlign);
if fmt.TextRotation <> trHorizontal then
Include(fmt.UsedFormattingFields, uffTextRotation);
FCellFormatList.Add(fmt);
end;
node := node.NextSibling;
end;
@@ -1003,8 +1076,8 @@ begin
fntStyles := fnt.Style;
fntColor := fnt.Color;
end else begin
fntName := DEFAULTFONTNAME;
fntSize := DEFAULTFONTSIZE;
fntName := DEFAULT_FONTNAME;
fntSize := DEFAULT_FONTSIZE;
fntStyles := [];
fntColor := scBlack;
end;
@@ -1245,14 +1318,19 @@ end;
procedure TsSpreadOOXMLReader.ReadSheetList(ANode: TDOMNode; AList: TStrings);
var
node: TDOMNode;
nodename: String;
sheetName: String;
//sheetId: String;
begin
node := ANode.FirstChild;
while node <> nil do begin
sheetName := GetAttrValue(node, 'name');
//sheetId := GetAttrValue(node, 'sheetId');
AList.Add(sheetName);
nodename := node.NodeName;
if nodename = 'sheet' then
begin
sheetName := GetAttrValue(node, 'name');
//sheetId := GetAttrValue(node, 'sheetId');
AList.Add(sheetName);
end;
node := node.NextSibling;
end;
end;
@@ -1521,6 +1599,7 @@ end;
- Bold styles for cells having UsedFormattingFileds = [uffBold]
All other styles will be added by "ListAllFormattingStyles".
}
(*
procedure TsSpreadOOXMLWriter.AddDefaultFormats();
// We store the index of the XF record that will be assigned to this style in
// the "row" of the style. Will be needed when writing the XF record.
@@ -1541,7 +1620,9 @@ begin
NextXFIndex := 2;
end;
*)
(*
{ Looks for the combination of border attributes of the given cell in the
FBorderList and returns its index. }
function TsSpreadOOXMLWriter.FindBorderInList(ACell: PCell): Integer;
@@ -1566,7 +1647,32 @@ begin
// Not found --> return -1
Result := -1;
end;
*)
{ Looks for the combination of border attributes of the given format record in
the FBorderList and returns its index. }
function TsSpreadOOXMLWriter.FindBorderInList(AFormat: PsCellFormat): Integer;
var
i: Integer;
fmt: PsCellFormat;
begin
// No cell, or border-less --> index 0
if (AFormat = nil) or not (uffBorder in AFormat.UsedFormattingFields) then begin
Result := 0;
exit;
end;
for i:=0 to High(FBorderList) do begin
fmt := FBorderList[i];
if SameCellBorders(fmt, AFormat) then begin
Result := i;
exit;
end;
end;
// Not found --> return -1
Result := -1;
end;
(*
{ Looks for the combination of fill attributes of the given cell in the
FFillList and returns its index. }
function TsSpreadOOXMLWriter.FindFillInList(ACell: PCell): Integer;
@@ -1593,11 +1699,46 @@ begin
// Not found --> return -1
Result := -1;
end;
*)
{ Looks for the combination of fill attributes of the given format record in the
FFillList and returns its index. }
function TsSpreadOOXMLWriter.FindFillInList(AFormat: PsCellFormat): Integer;
var
i: Integer;
fmt: PsCellFormat;
begin
if (AFormat = nil) or not (uffBackgroundColor in AFormat^.UsedFormattingFields)
then begin
Result := 0;
exit;
end;
// Index 0 is "no fill" which already has been handled.
// Index 1 is also pre-defined (gray 25%)
for i:=2 to High(FFillList) do begin
fmt := FFillList[i];
if (fmt <> nil) and (uffBackgroundColor in fmt^.UsedFormattingFields) then
if (AFormat^.BackgroundColor = fmt^.BackgroundColor) then
begin
Result := i;
exit;
end;
end;
// Not found --> return -1
Result := -1;
end;
{ Determines the formatting index which a given cell has in list of
"FormattingStyles" which correspond to the section cellXfs of the styles.xml
file. }
function TsSpreadOOXMLWriter.GetStyleIndex(ACell: PCell): Cardinal;
begin
Result := ACell^.FormatIndex;
end;
{
var
idx: Integer;
begin
@@ -1613,14 +1754,26 @@ end;
To be used for the styles.xml. }
procedure TsSpreadOOXMLWriter.ListAllBorders;
var
styleCell: PCell;
//styleCell: PCell;
i, n : Integer;
fmt: PsCellFormat;
begin
// first list entry is a no-border cell
SetLength(FBorderList, 1);
n := 1;
SetLength(FBorderList, n);
FBorderList[0] := nil;
n := 1;
for i := 0 to FWorkbook.GetNumCellFormats - 1 do
begin
fmt := FWorkbook.GetPointerToCellFormat(i);
if FindBorderInList(fmt) = -1 then
begin
SetLength(FBorderList, n+1);
FBorderList[n] := fmt;
inc(n);
end;
end;
{
for i := 0 to High(FFormattingStyles) do begin
styleCell := @FFormattingStyles[i];
if FindBorderInList(styleCell) = -1 then begin
@@ -1628,7 +1781,7 @@ begin
FBorderList[n] := styleCell;
inc(n);
end;
end;
end; }
end;
{ Creates a list of all fill styles found in the workbook.
@@ -1638,15 +1791,27 @@ end;
To be used for styles.xml. }
procedure TsSpreadOOXMLWriter.ListAllFills;
var
styleCell: PCell;
//styleCell: PCell;
i, n: Integer;
fmt: PsCellFormat;
begin
// Add built-in fills first.
SetLength(FFillList, 2);
n := 2;
SetLength(FFillList, n);
FFillList[0] := nil; // built-in "no fill"
FFillList[1] := nil; // built-in "gray125"
n := 2;
for i := 0 to FWorkbook.GetNumCellFormats - 1 do
begin
fmt := FWorkbook.GetPointerToCellFormat(i);
if FindFillInList(fmt) = -1 then
begin
SetLength(FFillList, n+1);
FFillList[n] := fmt;
inc(n);
end;
end;
{
for i := 0 to High(FFormattingStyles) do begin
styleCell := @FFormattingStyles[i];
if FindFillInList(styleCell) = -1 then begin
@@ -1655,6 +1820,7 @@ begin
inc(n);
end;
end;
}
end;
procedure TsSpreadOOXMLWriter.WriteBorderList(AStream: TStream);
@@ -1662,8 +1828,10 @@ const
LINESTYLE_NAME: Array[TsLineStyle] of String = (
'thin', 'medium', 'dashed', 'dotted', 'thick', 'double', 'hair');
procedure WriteBorderStyle(AStream: TStream; ACell: PCell; ABorder: TsCellBorder;
ABorderName: String);
//procedure WriteBorderStyle(AStream: TStream; ACell: PCell; ABorder: TsCellBorder;
// ABorderName: String);
procedure WriteBorderStyle(AStream: TStream; AFormatRecord: PsCellFormat;
ABorder: TsCellBorder; ABorderName: String);
{ border names found in xlsx files for Excel selections:
"thin", "hair", "dotted", "dashed", "dashDotDot", "dashDot", "mediumDashDotDot",
"slantDashDot", "mediumDashDot", "mediumDashed", "medium", "thick", "double" }
@@ -1672,11 +1840,15 @@ const
colorName: String;
rgb: TsColorValue;
begin
if (ABorder in ACell^.Border) then begin
//if (ABorder in ACell^.Border) then begin
if (ABorder in AFormatRecord^.Border) then begin
// Line style
styleName := LINESTYLE_NAME[ACell.BorderStyles[ABorder].LineStyle];
styleName := LINESTYLE_NAME[AFormatRecord^.BorderStyles[ABorder].LineStyle];
//styleName := LINESTYLE_NAME[ACell.BorderStyles[ABorder].LineStyle];
// Border color
rgb := Workbook.GetPaletteColor(ACell^.BorderStyles[ABorder].Color);
rgb := Workbook.GetPaletteColor(AFormatRecord^.BorderStyles[ABorder].Color);
//rgb := Workbook.GetPaletteColor(ACell^.BorderStyles[ABorder].Color);
colorName := ColorToHTMLColorStr(rgb, true);
AppendToStream(AStream, Format(
'<%s style="%s"><color rgb="%s" /></%s>',
@@ -1689,25 +1861,33 @@ const
var
i: Integer;
styleCell: PCell;
// styleCell: PCell;
diag: String;
begin
AppendToStream(AStream, Format(
'<borders count="%d">', [Length(FBorderList)]));
// index 0 -- build-in "no borders"
// index 0 -- built-in "no borders"
AppendToStream(AStream,
'<border>',
'<left /><right /><top /><bottom /><diagonal />',
'</border>');
for i:=1 to High(FBorderList) do begin
styleCell := FBorderList[i];
//styleCell := FBorderList[i];
diag := '';
if (cbDiagUp in FBorderList[i].Border) then diag := diag + ' diagonalUp="1"';
if (cbDiagDown in FBorderList[i].Border) then diag := diag + ' diagonalDown="1"';
AppendToStream(AStream,
'<border' + diag + '>');
WriteBorderStyle(AStream, FBorderList[i], cbWest, 'left');
WriteBorderStyle(AStream, FBorderList[i], cbEast, 'right');
WriteBorderStyle(AStream, FBorderList[i], cbNorth, 'top');
WriteBorderStyle(AStream, FBorderList[i], cbSouth, 'bottom');
// OOXML uses the same border style for both diagonals. In agreement with
// the biff implementation we select the style from the diagonal-up line.
WriteBorderStyle(AStream, FBorderList[i], cbDiagUp, 'diagonal');
{
WriteBorderStyle(AStream, styleCell, cbWest, 'left');
WriteBorderStyle(AStream, styleCell, cbEast, 'right');
WriteBorderStyle(AStream, styleCell, cbNorth, 'top');
@@ -1715,6 +1895,7 @@ begin
// OOXML uses the same border style for both diagonals. In agreement with
// the biff implementation we select the style from the diagonal-up line.
WriteBorderStyle(AStream, styleCell, cbDiagUp, 'diagonal');
}
AppendToStream(AStream,
'</border>');
end;
@@ -1750,7 +1931,7 @@ end;
procedure TsSpreadOOXMLWriter.WriteFillList(AStream: TStream);
var
i: Integer;
styleCell: PCell;
//styleCell: PCell;
rgb: TsColorValue;
begin
AppendToStream(AStream, Format(
@@ -1770,8 +1951,9 @@ begin
// user-defined fills
for i:=2 to High(FFillList) do begin
styleCell := FFillList[i];
rgb := Workbook.GetPaletteColor(styleCell^.BackgroundColor);
//styleCell := FFillList[i];
//rgb := Workbook.GetPaletteColor(styleCell^.BackgroundColor);
rgb := Workbook.GetPaletteColor(FFillList[i]^.BackgroundColor);
AppendToStream(AStream,
'<fill>',
'<patternFill patternType="solid">');
@@ -1861,7 +2043,7 @@ var
begin
s := '';
n := 0;
i := NumFormatList.FindByIndex(NumFormatList.FirstFormatIndexInFile);
i := NumFormatList.FindByIndex(NumFormatList.FirstNumFormatIndexInFile);
if i > -1 then begin
while i < NumFormatList.Count do begin
item := NumFormatList[i];
@@ -2083,27 +2265,36 @@ begin
end;
end;
{ Writes the style list which the writer has collected in FFormattingStyles. }
{ Writes the style list which the workbook has collected in its FormatList }
procedure TsSpreadOOXMLWriter.WriteStyleList(AStream: TStream; ANodeName: String);
var
styleCell: TCell;
// styleCell: TCell;
s, sAlign: String;
fontID: Integer;
numFmtId: Integer;
fillId: Integer;
borderId: Integer;
idx: Integer;
fmt: PsCellFormat;
i: Integer;
begin
AppendToStream(AStream, Format(
'<%s count="%d">', [ANodeName, Length(FFormattingStyles)]));
'<%s count="%d">', [ANodeName, FWorkbook.GetNumCellFormats]));
// '<%s count="%d">', [ANodeName, Length(FFormattingStyles)]));
for styleCell in FFormattingStyles do begin
// for styleCell in FFormattingStyles do begin
for i:=0 to FWorkbook.GetNumCellFormats-1 do
begin
fmt := FWorkbook.GetPointerToCellFormat(i);
s := '';
sAlign := '';
{ Number format }
if (uffNumberFormat in styleCell.UsedFormattingFields) then begin
idx := NumFormatList.FindFormatOf(@styleCell);
// if (uffNumberFormat in styleCell.UsedFormattingFields) then
if (uffNumberFormat in fmt^.UsedFormattingFields) then
begin
// idx := NumFormatList.FindFormatOf(@styleCell);
idx := NumFormatList.Find(fmt^.NumberFormat, fmt^.NumberFormatStr);
if idx > -1 then begin
numFmtID := NumFormatList[idx].Index;
s := s + Format('numFmtId="%d" applyNumberFormat="1" ', [numFmtId]);
@@ -2112,54 +2303,82 @@ begin
{ Font }
fontId := 0;
if (uffBold in fmt^.UsedFormattingFields) then
fontID := 1;
if (uffFont in fmt^.UsedFormattingFields) then
fontID := fmt^.FontIndex;
{
if (uffBold in styleCell.UsedFormattingFields) then
fontId := 1;
if (uffFont in styleCell.UsedFormattingFields) then
fontId := styleCell.FontIndex;
}
s := s + Format('fontId="%d" ', [fontId]);
if fontID > 0 then s := s + 'applyFont="1" ';
if ANodeName = 'cellXfs' then s := s + 'xfId="0" ';
{ Text rotation }
{
if (uffTextRotation in styleCell.UsedFormattingFields) and (styleCell.TextRotation <> trHorizontal)
then
case styleCell.TextRotation of
case styleCell.TextRotation of}
if (uffTextRotation in fmt^.UsedFormattingFields) then
case fmt^.TextRotation of
trHorizontal : ;
rt90DegreeClockwiseRotation : sAlign := sAlign + Format('textRotation="%d" ', [180]);
rt90DegreeCounterClockwiseRotation: sAlign := sAlign + Format('textRotation="%d" ', [90]);
rtStacked : sAlign := sAlign + Format('textRotation="%d" ', [255]);
end;
{ Text alignment }
{
if (uffHorAlign in styleCell.UsedFormattingFields) and (styleCell.HorAlignment <> haDefault)
then
case styleCell.HorAlignment of
case styleCell.HorAlignment of }
if (uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haDefault)
then
case fmt.HorAlignment of
haLeft : sAlign := sAlign + 'horizontal="left" ';
haCenter: sAlign := sAlign + 'horizontal="center" ';
haRight : sAlign := sAlign + 'horizontal="right" ';
end;
{
if (uffVertAlign in styleCell.UsedformattingFields) and (styleCell.VertAlignment <> vaDefault)
then
case styleCell.VertAlignment of
}
if (uffVertAlign in fmt^.UsedFormattingFields) and (fmt^.VertAlignment <> vaDefault)
then
case fmt.VertAlignment of
vaTop : sAlign := sAlign + 'vertical="top" ';
vaCenter: sAlign := sAlign + 'vertical="center" ';
vaBottom: sAlign := sAlign + 'vertical="bottom" ';
end;
if (uffWordWrap in styleCell.UsedFormattingFields) then
//if (uffWordWrap in styleCell.UsedFormattingFields) then
if (uffWordWrap in fmt^.UsedFormattingFields) then
sAlign := sAlign + 'wrapText="1" ';
{ Fill }
if (uffBackgroundColor in styleCell.UsedFormattingFields) then begin
fillID := FindFillInList(@styleCell);
{
if (uffBackgroundColor in styleCell.UsedFormattingFields) then
fillID := FindFillInList(@styleCell); }
if (uffBackgroundColor in fmt.UsedFormattingFields) then
begin
fillID := FindFillInList(fmt);
if fillID = -1 then fillID := 0;
s := s + Format('fillId="%d" applyFill="1" ', [fillID]);
end;
{ Border }
if (uffBorder in styleCell.UsedFormattingFields) then begin
borderID := FindBorderInList(@styleCell);
{
if (uffBorder in styleCell.UsedFormattingFields) then
borderID := FindBorderInList(@styleCell); }
if (uffBorder in fmt^.UsedFormattingFields) then
begin
borderID := FindBorderInList(fmt);
if borderID = -1 then borderID := 0;
s := s + Format('borderId="%d" applyBorder="1" ', [borderID]);
end;
@@ -2500,7 +2719,7 @@ var
begin
{ Analyze the workbook and collect all information needed }
ListAllNumFormats;
ListAllFormattingStyles;
//ListAllFormattingStyles;
ListAllFills;
ListAllBorders;
@@ -2546,7 +2765,6 @@ var
begin
cellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell);
AppendToStream(AStream, Format(
'<c r="%s" s="%d">', [CellPosText, lStyleIndex]),
'<v></v>',