fpspreadsheet: Major reconstructor of color management: no more palettes now, use direct rgb colors instead. May break existing code - sorry! Update all demos and unit tests (passed).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4156 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-05-28 20:08:24 +00:00
parent 46386a0f37
commit 545bd7ed0f
33 changed files with 1696 additions and 1025 deletions

View File

@ -49,7 +49,7 @@ begin
writeln('Finished.'); writeln('Finished.');
writeln; writeln;
writeln('Please open "'+OutputFile+'" in "fpsgrid".'); writeln('Please open "'+OutputFile+'" in "fpsgrid".');
writeLn('It should show calculation results in cells B1 and B2.'); writeLn('It must show correct calculation results in cells B1 and B2.');
finally finally
workbook.Free; workbook.Free;
end; end;

View File

@ -10,7 +10,7 @@ program excel5write;
{$mode delphi}{$H+} {$mode delphi}{$H+}
uses uses
Classes, SysUtils, fpsTypes, fpspreadsheet, xlsbiff5; Classes, SysUtils, fpsTypes, fpSpreadsheet, fpsPalette, fpsUtils, xlsbiff5;
const const
Str_First = 'First'; Str_First = 'First';
@ -28,6 +28,7 @@ var
i, r: Integer; i, r: Integer;
number: Double; number: Double;
fmt: string; fmt: string;
palette: TsPalette;
begin begin
MyDir := ExtractFilePath(ParamStr(0)); MyDir := ExtractFilePath(ParamStr(0));
@ -359,10 +360,16 @@ begin
// Creates a new worksheet // Creates a new worksheet
MyWorksheet := MyWorkbook.AddWorksheet('Colors'); MyWorksheet := MyWorkbook.AddWorksheet('Colors');
for i:=0 to MyWorkbook.GetPaletteSize-1 do begin palette := TsPalette.Create;
try
palette.UseColors(PALETTE_BIFF5); // This stores the colors of BIFF5 files in the local palette
for i:=0 to palette.Count-1 do begin
MyWorksheet.WriteBlank(i, 0); MyWorksheet.WriteBlank(i, 0);
Myworksheet.WriteBackgroundColor(i, 0, TsColor(i)); Myworksheet.WriteBackgroundColor(i, 0, palette[i]);
MyWorksheet.WriteUTF8Text(i, 1, MyWorkbook.GetColorName(i)); MyWorksheet.WriteUTF8Text(i, 1, GetColorName(palette[i]));
end;
finally
palette.Free;
end; end;
// Save the spreadsheet to a file // Save the spreadsheet to a file

View File

@ -36,7 +36,6 @@ begin
// Create the spreadsheet // Create the spreadsheet
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
MyWorkbook.SetDefaultFont('Calibri', 9); MyWorkbook.SetDefaultFont('Calibri', 9);
MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
MyWorkbook.FormatSettings.CurrencyFormat := 2; MyWorkbook.FormatSettings.CurrencyFormat := 2;
MyWorkbook.FormatSettings.NegCurrFormat := 14; MyWorkbook.FormatSettings.NegCurrFormat := 14;
MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving]; MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving];

View File

@ -135,8 +135,8 @@ begin
MyWorksheet.WriteNumber(row, 0, row); MyWorksheet.WriteNumber(row, 0, row);
MyWorksheet.WriteUTF8Text(row, 1, 'RGB background color:'); MyWorksheet.WriteUTF8Text(row, 1, 'RGB background color:');
MyWorksheet.WriteUTF8Text(row, 2, 'color #FF77C3'); MyWorksheet.WriteUTF8Text(row, 2, 'color #FF77C3'); // HTML colors are big-endian
MyWorksheet.WriteBackgroundColor(row, 2, MyWorkbook.AddColorToPalette($C377FF)); MyWorksheet.WriteBackgroundColor(row, 2, $C377FF); // fps colors are little-endian
inc(row); inc(row);
MyWorksheet.WriteNumber(row, 0, row); MyWorksheet.WriteNumber(row, 0, row);

View File

@ -9,6 +9,7 @@ object MainFrm: TMainFrm
Menu = MainMenu Menu = MainMenu
OnActivate = FormActivate OnActivate = FormActivate
OnCreate = FormCreate OnCreate = FormCreate
OnDestroy = FormDestroy
ShowHint = True ShowHint = True
LCLVersion = '1.5' LCLVersion = '1.5'
object Panel1: TPanel object Panel1: TPanel
@ -425,7 +426,7 @@ object MainFrm: TMainFrm
end end
end end
object InspectorSplitter: TSplitter object InspectorSplitter: TSplitter
Left = 639 Left = 591
Height = 453 Height = 453
Top = 84 Top = 84
Width = 5 Width = 5
@ -433,10 +434,10 @@ object MainFrm: TMainFrm
ResizeAnchor = akRight ResizeAnchor = akRight
end end
object InspectorPageControl: TPageControl object InspectorPageControl: TPageControl
Left = 644 Left = 596
Height = 453 Height = 453
Top = 84 Top = 84
Width = 241 Width = 289
ActivePage = PgCellValue ActivePage = PgCellValue
Align = alRight Align = alRight
TabIndex = 0 TabIndex = 0
@ -445,18 +446,20 @@ object MainFrm: TMainFrm
object PgCellValue: TTabSheet object PgCellValue: TTabSheet
Caption = 'Cell value' Caption = 'Cell value'
ClientHeight = 425 ClientHeight = 425
ClientWidth = 233 ClientWidth = 281
object CellInspector: TValueListEditor object CellInspector: TValueListEditor
Left = 0 Left = 0
Height = 425 Height = 425
Top = 0 Top = 0
Width = 233 Width = 281
Align = alClient Align = alClient
FixedCols = 0 FixedCols = 0
MouseWheelOption = mwGrid
RowCount = 15 RowCount = 15
TabOrder = 0 TabOrder = 0
TitleStyle = tsNative TitleStyle = tsNative
DisplayOptions = [doColumnTitles, doAutoColResize] DisplayOptions = [doColumnTitles]
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goColSizing, goAlwaysShowEditor, goThumbTracking]
Strings.Strings = ( Strings.Strings = (
'Row=' 'Row='
'Column=' 'Column='
@ -478,8 +481,8 @@ object MainFrm: TMainFrm
'' ''
) )
ColWidths = ( ColWidths = (
114 138
115 139
) )
end end
end end
@ -494,7 +497,7 @@ object MainFrm: TMainFrm
Left = 0 Left = 0
Height = 453 Height = 453
Top = 84 Top = 84
Width = 639 Width = 591
OnChange = TabControlChange OnChange = TabControlChange
Align = alClient Align = alClient
TabOrder = 3 TabOrder = 3
@ -502,7 +505,7 @@ object MainFrm: TMainFrm
Left = 2 Left = 2
Height = 448 Height = 448
Top = 3 Top = 3
Width = 635 Width = 587
FrozenCols = 0 FrozenCols = 0
FrozenRows = 0 FrozenRows = 0
ReadFormulas = False ReadFormulas = False

View File

@ -8,7 +8,7 @@ uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, Menus, ExtCtrls, ComCtrls, ActnList, Spin, Grids, StdCtrls, Menus, ExtCtrls, ComCtrls, ActnList, Spin, Grids,
ColorBox, ValEdit, ColorBox, ValEdit,
fpstypes, fpspreadsheetgrid, fpspreadsheet, fpstypes, fpspalette, fpspreadsheetgrid, fpspreadsheet,
{%H-}fpsallformats; {%H-}fpsallformats;
type type
@ -325,6 +325,7 @@ type
procedure FontSizeComboBoxSelect(Sender: TObject); procedure FontSizeComboBoxSelect(Sender: TObject);
procedure FormActivate(Sender: TObject); procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure InspectorPageControlChange(Sender: TObject); procedure InspectorPageControlChange(Sender: TObject);
procedure MemoFormulaEditingDone(Sender: TObject); procedure MemoFormulaEditingDone(Sender: TObject);
procedure TabControlChange(Sender: TObject); procedure TabControlChange(Sender: TObject);
@ -334,6 +335,7 @@ type
private private
FCopiedFormat: TCell; FCopiedFormat: TCell;
FPalette: TsPalette;
function EditComment(ACaption: String; var AText: String): Boolean; function EditComment(ACaption: String; var AText: String): Boolean;
procedure LoadFile(const AFileName: String); procedure LoadFile(const AFileName: String);
@ -928,9 +930,9 @@ begin
if WorksheetGrid.Workbook <> nil then begin if WorksheetGrid.Workbook <> nil then begin
Items.Clear; Items.Clear;
Items.AddObject('no fill', TObject(PtrInt(clNone))); Items.AddObject('no fill', TObject(PtrInt(clNone)));
for i:=0 to WorksheetGrid.Workbook.GetPaletteSize-1 do begin for i:=0 to FPalette.Count-1 do begin
clr := WorksheetGrid.Workbook.GetPaletteColor(i); clr := FPalette[i];
clrName := WorksheetGrid.Workbook.GetColorName(i); clrName := GetColorName(clr);
Items.AddObject(Format('%d: %s', [i, clrName]), TObject(PtrInt(clr))); Items.AddObject(Format('%d: %s', [i, clrName]), TObject(PtrInt(clr)));
end; end;
end; end;
@ -947,7 +949,7 @@ begin
if CbBackgroundColor.ItemIndex <= 0 then if CbBackgroundColor.ItemIndex <= 0 then
with WorksheetGrid do BackgroundColors[Selection] := scNotDefined with WorksheetGrid do BackgroundColors[Selection] := scNotDefined
else else
with WorksheetGrid do BackgroundColors[Selection] := CbBackgroundColor.ItemIndex - 1; with WorksheetGrid do BackgroundColors[Selection] := PtrInt(CbBackgroundColor.Items.Objects[CbBackgroundColor.ItemIndex]);
end; end;
procedure TMainFrm.CbHeaderStyleChange(Sender: TObject); procedure TMainFrm.CbHeaderStyleChange(Sender: TObject);
@ -1087,12 +1089,20 @@ begin
FontSizeCombobox.DropDownCount := DROPDOWN_COUNT; FontSizeCombobox.DropDownCount := DROPDOWN_COUNT;
CbBackgroundColor.DropDownCount := DROPDOWN_COUNT; CbBackgroundColor.DropDownCount := DROPDOWN_COUNT;
FPalette := TsPalette.Create;
FPalette.AddExcelColors;
// Initialize a new empty workbook // Initialize a new empty workbook
AcNewExecute(nil); AcNewExecute(nil);
ActiveControl := WorksheetGrid; ActiveControl := WorksheetGrid;
end; end;
procedure TMainFrm.FormDestroy(Sender: TObject);
begin
FPalette.Free;
end;
procedure TMainFrm.InspectorPageControlChange(Sender: TObject); procedure TMainFrm.InspectorPageControlChange(Sender: TObject);
begin begin
CellInspector.Parent := InspectorPageControl.ActivePage; CellInspector.Parent := InspectorPageControl.ActivePage;
@ -1184,13 +1194,14 @@ end;
procedure TMainFrm.UpdateBackgroundColorIndex; procedure TMainFrm.UpdateBackgroundColorIndex;
var var
sClr: TsColor; clr: TsColor;
begin begin
with WorksheetGrid do sClr := BackgroundColors[Selection]; with WorksheetGrid do
if sClr = scNotDefined then clr := BackgroundColors[Selection];
if (clr = scNotDefined) or (clr = scTransparent) then
CbBackgroundColor.ItemIndex := 0 // no fill CbBackgroundColor.ItemIndex := 0 // no fill
else else
CbBackgroundColor.ItemIndex := sClr + 1; CbBackgroundColor.ItemIndex := CbBackgroundColor.Items.IndexOfObject(TObject(PtrInt(clr)));
end; end;
procedure TMainFrm.UpdateHorAlignmentActions; procedure TMainFrm.UpdateHorAlignmentActions;
@ -1214,6 +1225,7 @@ var
cb: TsCellBorder; cb: TsCellBorder;
r1,r2,c1,c2: Cardinal; r1,r2,c1,c2: Cardinal;
fmt: TsCellFormat; fmt: TsCellFormat;
nfparams: TsNumFormatParams;
begin begin
with CellInspector do with CellInspector do
begin begin
@ -1223,10 +1235,10 @@ begin
if InspectorPageControl.ActivePage = PgCellValue then if InspectorPageControl.ActivePage = PgCellValue then
begin begin
if ACell=nil if ACell=nil
then Strings.Add('Row=') then Strings.Add(Format('Row=%d', [WorksheetGrid.GetWorksheetRow(WorksheetGrid.Row)]))
else Strings.Add(Format('Row=%d', [ACell^.Row])); else Strings.Add(Format('Row=%d', [ACell^.Row]));
if ACell=nil if ACell=nil
then Strings.Add('Column=') then Strings.Add(Format('Column=%d', [WorksheetGrid.GetWorksheetCol(WorksheetGrid.Col)]))
else Strings.Add(Format('Column=%d', [ACell^.Col])); else Strings.Add(Format('Column=%d', [ACell^.Col]));
if ACell=nil if ACell=nil
then Strings.Add('ContentType=') then Strings.Add('ContentType=')
@ -1308,29 +1320,40 @@ begin
else else
Strings.Add(Format('BorderStyles[%s]=%s, %s', [ Strings.Add(Format('BorderStyles[%s]=%s, %s', [
GetEnumName(TypeInfo(TsCellBorder), ord(cb)), GetEnumName(TypeInfo(TsCellBorder), ord(cb)),
GetEnumName(TypeInfo(TsLineStyle), ord(fmt.BorderStyles[cbEast].LineStyle)), GetEnumName(TypeInfo(TsLineStyle), ord(fmt.BorderStyles[cb].LineStyle)),
WorksheetGrid.Workbook.GetColorName(fmt.BorderStyles[cbEast].Color) GetColorName(fmt.BorderStyles[cb].Color)
])); ]));
if (ACell=nil) or not (uffBackground in fmt.UsedformattingFields) if (ACell=nil) or not (uffBackground in fmt.UsedformattingFields)
then Strings.Add('BackgroundColor=') then Strings.Add('BackgroundColor=')
else Strings.Add(Format('BackgroundColor=%d (%s)', [ else Strings.Add(Format('BackgroundColor=$%8x (%s)', [
fmt.Background.BgColor, fmt.Background.BgColor,
WorksheetGrid.Workbook.GetColorName(fmt.Background.BgColor) GetColorName(fmt.Background.BgColor)
])); ]));
if (ACell=nil) or not (uffNumberFormat in fmt.UsedFormattingFields) if (ACell=nil) or not (uffNumberFormat in fmt.UsedFormattingFields)
then Strings.Add('NumberFormat=') then Strings.Add('NumberFormat=')
else Strings.Add(Format('NumberFormat=%s', [GetEnumName(TypeInfo(TsNumberFormat), ord(fmt.NumberFormat))])); else begin
nfparams := WorksheetGrid.Workbook.GetNumberFormat(fmt.NumberFormatIndex);
if nfparams = nil then
begin
Strings.Add('NumberFormat=General');
Strings.Add('NumberFormatStr=');
end else
begin
Strings.Add(Format('NumberFormat=%s', [GetEnumName(TypeInfo(TsNumberFormat), ord(nfparams.NumFormat))]));
Strings.Add(Format('NumberFormatStr=%s', [nfparams.NumFormatStr]));
end;
end;
{
if (ACell=nil) or not (uffNumberFormat in fmt.UsedFormattingFields) if (ACell=nil) or not (uffNumberFormat in fmt.UsedFormattingFields)
then Strings.Add('NumberFormatStr=') then Strings.Add('NumberFormatStr=')
else Strings.Add('NumberFormatStr=' + fmt.NumberFormatStr); else Strings.Add('NumberFormatStr=' + fmt.NumberFormatStr);
if not WorksheetGrid.Worksheet.IsMerged(ACell) then }
Strings.Add('Merged range=') if not WorksheetGrid.Worksheet.IsMerged(ACell)
else then Strings.Add('Merged range=')
begin else begin
WorksheetGrid.Worksheet.FindMergedRange(ACell, r1, c1, r2, c2); WorksheetGrid.Worksheet.FindMergedRange(ACell, r1, c1, r2, c2);
Strings.Add('Merged range=' + GetCellRangeString(r1, c1, r2, c2)); Strings.Add('Merged range=' + GetCellRangeString(r1, c1, r2, c2));
end; end;
end; end;
end; end;
end; end;

View File

@ -103,7 +103,6 @@
<ComponentName Value="MainFrm"/> <ComponentName Value="MainFrm"/>
<HasResources Value="True"/> <HasResources Value="True"/>
<ResourceBaseClass Value="Form"/> <ResourceBaseClass Value="Form"/>
<UnitName Value="mainform"/>
</Unit1> </Unit1>
<Unit2> <Unit2>
<Filename Value="..\shared\scsvparamsform.pas"/> <Filename Value="..\shared\scsvparamsform.pas"/>
@ -128,6 +127,7 @@
<Unit5> <Unit5>
<Filename Value="..\shared\sctrls.pas"/> <Filename Value="..\shared\sctrls.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="sCtrls"/>
</Unit5> </Unit5>
<Unit6> <Unit6>
<Filename Value="..\shared\scurrencyform.pas"/> <Filename Value="..\shared\scurrencyform.pas"/>

View File

@ -9,6 +9,7 @@ object MainFrm: TMainFrm
Menu = MainMenu Menu = MainMenu
OnActivate = FormActivate OnActivate = FormActivate
OnCreate = FormCreate OnCreate = FormCreate
OnDestroy = FormDestroy
ShowHint = True ShowHint = True
LCLVersion = '1.5' LCLVersion = '1.5'
object MainToolBar: TToolBar object MainToolBar: TToolBar

View File

@ -9,7 +9,7 @@ uses
StdCtrls, Menus, ExtCtrls, ComCtrls, ActnList, Grids, ColorBox, SynEdit, StdCtrls, Menus, ExtCtrls, ComCtrls, ActnList, Grids, ColorBox, SynEdit,
SynEditHighlighter, SynHighlighterHTML, SynHighlighterMulti, SynEditHighlighter, SynHighlighterHTML, SynHighlighterMulti,
SynHighlighterCss, SynGutterCodeFolding, fpspreadsheetgrid, SynHighlighterCss, SynGutterCodeFolding, fpspreadsheetgrid,
fpstypes, fpspreadsheet, fpsallformats; fpstypes, fpspalette, fpspreadsheet, fpsallformats;
type type
@ -187,6 +187,7 @@ type
procedure FontSizeComboBoxSelect(Sender: TObject); procedure FontSizeComboBoxSelect(Sender: TObject);
procedure FormActivate(Sender: TObject); procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PageControlChange(Sender: TObject); procedure PageControlChange(Sender: TObject);
procedure TabControlChange(Sender: TObject); procedure TabControlChange(Sender: TObject);
procedure WorksheetGridSelection(Sender: TObject; aCol, aRow: Integer); procedure WorksheetGridSelection(Sender: TObject; aCol, aRow: Integer);
@ -194,6 +195,7 @@ type
WorksheetGrid: TsWorksheetGrid; WorksheetGrid: TsWorksheetGrid;
FCopiedFormat: TCell; FCopiedFormat: TCell;
FHighlighter: TSynCustomHighlighter; FHighlighter: TSynCustomHighlighter;
FPalette: TsPalette;
procedure LoadFile(const AFileName: String); procedure LoadFile(const AFileName: String);
procedure SetupBackgroundColorBox; procedure SetupBackgroundColorBox;
procedure UpdateBackgroundColorIndex; procedure UpdateBackgroundColorIndex;
@ -543,20 +545,22 @@ begin
if (WorksheetGrid <> nil) and (WorksheetGrid.Workbook <> nil) then begin if (WorksheetGrid <> nil) and (WorksheetGrid.Workbook <> nil) then begin
Items.Clear; Items.Clear;
Items.AddObject('no fill', TObject(PtrInt(clNone))); Items.AddObject('no fill', TObject(PtrInt(clNone)));
for i:=0 to WorksheetGrid.Workbook.GetPaletteSize-1 do begin for i:=0 to FPalette.Count-1 do begin
clr := WorksheetGrid.Workbook.GetPaletteColor(i); clr := FPalette[i];
clrName := WorksheetGrid.Workbook.GetColorName(i); clrName := GetColorName(clr);
Items.AddObject(Format('%d: %s', [i, clrName]), TObject(PtrInt(clr))); Items.AddObject(Format('%d: %s', [i, clrName]), TObject(PtrInt(clr)));
end; end;
end; end;
end; end;
procedure TMainFrm.CbBackgroundColorSelect(Sender: TObject); procedure TMainFrm.CbBackgroundColorSelect(Sender: TObject);
var
clr: TsColor;
begin begin
if CbBackgroundColor.ItemIndex <= 0 then if CbBackgroundColor.ItemIndex <= 0 then
with WorksheetGrid do BackgroundColors[Selection] := scNotDefined with WorksheetGrid do BackgroundColors[Selection] := scNotDefined
else else
with WorksheetGrid do BackgroundColors[Selection] := CbBackgroundColor.ItemIndex - 1; with WorksheetGrid do BackgroundColors[Selection] := PtrInt(CbBackgroundColor.Items.Objects[CbBackgroundColor.ItemIndex]);
end; end;
procedure TMainFrm.FontComboBoxSelect(Sender: TObject); procedure TMainFrm.FontComboBoxSelect(Sender: TObject);
@ -637,6 +641,9 @@ begin
CbBackgroundColor.ColorRectWidth := CbBackgroundColor.ItemHeight - 6; // to get a square box... CbBackgroundColor.ColorRectWidth := CbBackgroundColor.ItemHeight - 6; // to get a square box...
{$ENDIF} {$ENDIF}
FPalette := TsPalette.Create;
FPalette.AddExcelColors;
// Initialize a new empty workbook // Initialize a new empty workbook
AcNewExecute(nil); AcNewExecute(nil);
@ -645,6 +652,11 @@ begin
ActiveControl := WorksheetGrid; ActiveControl := WorksheetGrid;
end; end;
procedure TMainFrm.FormDestroy(Sender: TObject);
begin
FPalette.Free;
end;
procedure TMainFrm.PageControlChange(Sender: TObject); procedure TMainFrm.PageControlChange(Sender: TObject);
var var
stream: TMemoryStream; stream: TMemoryStream;
@ -726,13 +738,13 @@ end;
procedure TMainFrm.UpdateBackgroundColorIndex; procedure TMainFrm.UpdateBackgroundColorIndex;
var var
sClr: TsColor; clr: TsColor;
begin begin
with WorksheetGrid do sClr := BackgroundColors[Selection]; with WorksheetGrid do clr := BackgroundColors[Selection];
if sClr = scNotDefined then if (clr = scNotDefined) or (clr = scTransparent) then
CbBackgroundColor.ItemIndex := 0 // no fill CbBackgroundColor.ItemIndex := 0 // no fill
else else
CbBackgroundColor.ItemIndex := sClr + 1; CbBackgroundColor.ItemIndex := CbBackgroundColor.Items.IndexOfObject(TObject(PtrInt(clr)));
end; end;
procedure TMainFrm.UpdateHorAlignmentActions; procedure TMainFrm.UpdateHorAlignmentActions;

View File

@ -1078,14 +1078,14 @@ procedure TsActionBorder.ApplyStyle(AWorkbook: TsWorkbook;
out ABorderStyle: TsCellBorderStyle); out ABorderStyle: TsCellBorderStyle);
begin begin
ABorderStyle.LineStyle := FLineStyle; ABorderStyle.LineStyle := FLineStyle;
ABorderStyle.Color := AWorkbook.GetPaletteColor(ABorderStyle.Color); ABorderStyle.Color := ABorderStyle.Color and $00FFFFFF;
end; end;
procedure TsActionBorder.ExtractStyle(AWorkbook: TsWorkbook; procedure TsActionBorder.ExtractStyle(AWorkbook: TsWorkbook;
ABorderStyle: TsCellBorderStyle); ABorderStyle: TsCellBorderStyle);
begin begin
FLineStyle := ABorderStyle.LineStyle; FLineStyle := ABorderStyle.LineStyle;
Color := AWorkbook.AddColorToPalette(ABorderStyle.Color); Color := ColorToRGB(ABorderStyle.Color);
end; end;
constructor TsActionBorders.Create; constructor TsActionBorders.Create;
@ -1575,14 +1575,14 @@ end;
procedure TsBackgroundColorDialogAction.DoAccept; procedure TsBackgroundColorDialogAction.DoAccept;
begin begin
FBackgroundColor := Workbook.AddColorToPalette(TsColorValue(Dialog.Color)); FBackgroundColor := ColorToRgb(Dialog.Color);
inherited; inherited;
end; end;
procedure TsBackgroundColorDialogAction.DoBeforeExecute; procedure TsBackgroundColorDialogAction.DoBeforeExecute;
begin begin
inherited; inherited;
Dialog.Color := Workbook.GetPaletteColor(FBackgroundColor); Dialog.Color := FBackgroundColor and $00FFFFFF;
end; end;
procedure TsBackgroundColorDialogAction.ExtractFromCell(ACell: PCell); procedure TsBackgroundColorDialogAction.ExtractFromCell(ACell: PCell);

View File

@ -20,11 +20,11 @@ type
FontName: String; FontName: String;
Size: Double; Size: Double;
Style: TsHeaderFooterFontStyles; Style: TsHeaderFooterFontStyles;
Color: TsColorValue; Color: TsColor;
constructor Create; overload; constructor Create; overload;
constructor Create(AFont: TsFont); overload; constructor Create(AFont: TsFont); overload;
constructor Create(AFontName: String; ASize: Double; constructor Create(AFontName: String; ASize: Double;
AStyle: TsHeaderFooterFontStyles; AColor: TsColorValue); overload; AStyle: TsHeaderFooterFontStyles; AColor: TsColor); overload;
procedure Assign(AFont: TObject); procedure Assign(AFont: TObject);
end; end;
@ -99,7 +99,7 @@ begin
end; end;
constructor TsHeaderFooterFont.Create(AFontName: String; ASize: Double; constructor TsHeaderFooterFont.Create(AFontName: String; ASize: Double;
AStyle: TsHeaderFooterFontStyles; AColor: TsColorValue); AStyle: TsHeaderFooterFontStyles; AColor: TsColor);
begin begin
FontName := AFontName; FontName := AFontName;
Size := ASize; Size := ASize;

View File

@ -108,7 +108,7 @@ type
function ReadFont(ANode: TDOMnode; APreferredIndex: Integer = -1): Integer; function ReadFont(ANode: TDOMnode; APreferredIndex: Integer = -1): Integer;
procedure ReadHeaderFooterFont(ANode: TDOMNode; var AFontName: String; procedure ReadHeaderFooterFont(ANode: TDOMNode; var AFontName: String;
var AFontSize: Double; var AFontStyle: TsHeaderFooterFontStyles; var AFontSize: Double; var AFontStyle: TsHeaderFooterFontStyles;
var AFontColor: TsColorValue); var AFontColor: TsColor);
function ReadHeaderFooterText(ANode: TDOMNode): String; function ReadHeaderFooterText(ANode: TDOMNode): String;
procedure ReadRowsAndCells(ATableNode: TDOMNode); procedure ReadRowsAndCells(ATableNode: TDOMNode);
procedure ReadRowStyle(AStyleNode: TDOMNode); procedure ReadRowStyle(AStyleNode: TDOMNode);
@ -561,7 +561,7 @@ var
n: Integer; n: Integer;
el, nEl: Integer; el, nEl: Integer;
ns: Integer; ns: Integer;
clr: TsColorvalue; clr: TsColor;
mask: String; mask: String;
timeIntervalStr: String; timeIntervalStr: String;
styleMapStr: String; styleMapStr: String;
@ -607,7 +607,7 @@ begin
case Elements[el].Token of case Elements[el].Token of
nftColor: nftColor:
begin begin
clr := FWorkbook.GetPaletteColor(Elements[el].IntValue); clr := TsColor(Elements[el].IntValue);
Result := Result + '<style:text-properties fo:color="' + ColorToHTMLColorStr(clr) + '" />'; Result := Result + '<style:text-properties fo:color="' + ColorToHTMLColorStr(clr) + '" />';
end; end;
@ -679,7 +679,9 @@ begin
// Mixed fraction // Mixed fraction
if nfkFraction in Kind then if nfkFraction in Kind then
begin begin
int := Elements[el].IntValue; if Elements[el].Token = nftIntOptDigit
then int := 0
else int := Elements[el].IntValue;
inc(el); inc(el);
while (el < nel) and not while (el < nel) and not
(Elements[el].Token in [nftFracNumZeroDigit, nftFracNumOptDigit, nftFracNumSpaceDigit]) (Elements[el].Token in [nftFracNumZeroDigit, nftFracNumOptDigit, nftFracNumSpaceDigit])
@ -874,8 +876,6 @@ begin
FMasterPageList := TFPList.Create; FMasterPageList := TFPList.Create;
FHeaderFooterFontList := TObjectList.Create; // frees objects FHeaderFooterFontList := TObjectList.Create; // frees objects
// Set up the default palette in order to have the default color names correct.
Workbook.UseDefaultPalette;
// Initial base date in case it won't be read from file // Initial base date in case it won't be read from file
FDateMode := dm1899; FDateMode := dm1899;
end; end;
@ -1129,7 +1129,7 @@ var
fntName: String; fntName: String;
fntSize: Double; fntSize: Double;
fntStyle: TsHeaderFooterFontStyles; fntStyle: TsHeaderFooterFontStyles;
fntColor: TsColorValue; fntColor: TsColor;
begin begin
if not Assigned(AStylesNode) then if not Assigned(AStylesNode) then
exit; exit;
@ -1683,7 +1683,7 @@ begin
s := GetAttrValue(ANode, 'fo:color'); s := GetAttrValue(ANode, 'fo:color');
if s <> '' then if s <> '' then
fntColor := FWorkbook.AddColorToPalette(HTMLColorStrToColor(s)) fntColor := HTMLColorStrToColor(s)
else else
fntColor := FWorkbook.GetDefaultFont.Color; fntColor := FWorkbook.GetDefaultFont.Color;
@ -1694,10 +1694,6 @@ begin
end else end else
if (APreferredIndex > -1) then if (APreferredIndex > -1) then
begin begin
{ --- wp: No more missing font #4 now !!!
if (APreferredIndex = 4) then
raise Exception.Create('Cannot replace font #4');
}
FWorkbook.ReplaceFont(APreferredIndex, fntName, fntSize, fntStyles, fntColor); FWorkbook.ReplaceFont(APreferredIndex, fntName, fntSize, fntStyles, fntColor);
Result := APreferredIndex; Result := APreferredIndex;
end else end else
@ -1938,7 +1934,7 @@ end;
procedure TsSpreadOpenDocReader.ReadHeaderFooterFont(ANode: TDOMNode; procedure TsSpreadOpenDocReader.ReadHeaderFooterFont(ANode: TDOMNode;
var AFontName: String; var AFontSize: Double; var AFontName: String; var AFontSize: Double;
var AFontStyle: TsHeaderFooterFontStyles; var AFontColor: TsColorValue); var AFontStyle: TsHeaderFooterFontStyles; var AFontColor: TsColor);
var var
s: String; s: String;
begin begin
@ -2241,7 +2237,7 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
grouping: Boolean; grouping: Boolean;
nex: Integer; nex: Integer;
cs: String; cs: String;
color: TsColorValue; color: TsColor;
hasColor: Boolean; hasColor: Boolean;
idx: Integer; idx: Integer;
begin begin
@ -2280,14 +2276,14 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
begin begin
nf := nfFraction; nf := nfFraction;
s := GetAttrValue(node, 'number:min-integer-digits'); s := GetAttrValue(node, 'number:min-integer-digits');
if s <> '' then fracInt := StrToInt(s) else fracInt := 0; if s <> '' then fracInt := StrToInt(s) else fracInt := -1;
s := GetAttrValue(node, 'number:min-numerator-digits'); s := GetAttrValue(node, 'number:min-numerator-digits');
if s <> '' then fracNum := StrToInt(s) else fracNum := 0; if s <> '' then fracNum := StrToInt(s) else fracNum := 0;
s := GetAttrValue(node, 'number:min-denominator-digits'); s := GetAttrValue(node, 'number:min-denominator-digits');
if s <> '' then fracDenom := StrToInt(s) else fracDenom := 0; if s <> '' then fracDenom := StrToInt(s) else fracDenom := 0;
s := GetAttrValue(node, 'number:denominator-value'); s := GetAttrValue(node, 'number:denominator-value');
if s <> '' then fracDenom := -StrToInt(s); if s <> '' then fracDenom := -StrToInt(s);
nfs := nfs + BuildFractionFormatString(fracInt > 0, fracNum, fracDenom); nfs := nfs + BuildFractionFormatString(fracInt > -1, fracNum, fracDenom);
end else end else
if nodeName = 'number:scientific-number' then if nodeName = 'number:scientific-number' then
begin begin
@ -2324,14 +2320,12 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
if s <> '' then if s <> '' then
begin begin
hasColor := true; hasColor := true;
// { // currently not needed
color := HTMLColorStrToColor(s); color := HTMLColorStrToColor(s);
idx := FWorkbook.AddColorToPalette(color); case color of
if idx < 8 then scBlack, scWhite, scRed, scGreen,
nfs := Format('[%s]%s', [FWorkbook.GetColorName(idx), nfs]) scBlue, scYellow, scMagenta, scCyan:
else nfs := Format('[%s]%s', [GetColorName(color), nfs]);
nfs := Format('[Color%d]%s', [idx, nfs]); end;
// }
end; end;
end; end;
node := node.NextSibling; node := node.NextSibling;
@ -2913,7 +2907,7 @@ var
numFmtStr: String; numFmtStr: String;
numFmtIndex: Integer; numFmtIndex: Integer;
numFmtParams: TsNumFormatParams; numFmtParams: TsNumFormatParams;
clr: TsColorValue; clr: TsColor;
s: String; s: String;
procedure SetBorderStyle(ABorder: TsCellBorder; AStyleValue: String); procedure SetBorderStyle(ABorder: TsCellBorder; AStyleValue: String);
@ -2925,7 +2919,7 @@ var
s: String; s: String;
wid: Double; wid: Double;
linestyle: String; linestyle: String;
rgb: TsColorValue; rgb: TsColor;
p: Integer; p: Integer;
begin begin
L := TStringList.Create; L := TStringList.Create;
@ -2934,7 +2928,7 @@ var
L.StrictDelimiter := true; L.StrictDelimiter := true;
L.DelimitedText := AStyleValue; L.DelimitedText := AStyleValue;
wid := 0; wid := 0;
rgb := TsColorValue(-1); rgb := scNotDefined;
linestyle := ''; linestyle := '';
for i:=0 to L.Count-1 do for i:=0 to L.Count-1 do
begin begin
@ -2981,8 +2975,7 @@ var
else else
if (linestyle = 'double') then if (linestyle = 'double') then
fmt.BorderStyles[ABorder].LineStyle := lsDouble; fmt.BorderStyles[ABorder].LineStyle := lsDouble;
fmt.BorderStyles[ABorder].Color := IfThen(rgb = TsColorValue(-1), fmt.BorderStyles[ABorder].Color := IfThen(rgb = scNotDefined, scBlack, rgb);
scBlack, Workbook.AddColorToPalette(rgb));
finally finally
L.Free; L.Free;
end; end;
@ -3048,10 +3041,6 @@ begin
fmt.FontIndex := ReadFont(styleChildNode, HYPERLINK_FONTINDEX) fmt.FontIndex := ReadFont(styleChildNode, HYPERLINK_FONTINDEX)
else else
fmt.FontIndex := ReadFont(styleChildNode); fmt.FontIndex := ReadFont(styleChildNode);
{
if fmt.FontIndex = BOLD_FONTINDEX then
Include(fmt.UsedFormattingFields, uffBold)
else }
if fmt.FontIndex > 0 then if fmt.FontIndex > 0 then
Include(fmt.UsedFormattingFields, uffFont); Include(fmt.UsedFormattingFields, uffFont);
end else end else
@ -3062,8 +3051,7 @@ begin
if (s <> '') and (s <> 'transparent') then begin if (s <> '') and (s <> 'transparent') then begin
clr := HTMLColorStrToColor(s); clr := HTMLColorStrToColor(s);
// ODS does not support background fill patterns! // ODS does not support background fill patterns!
fmt.Background.FgColor := IfThen(clr = TsColorValue(-1), fmt.Background.FgColor := IfThen(clr = scNotDefined, scTransparent, clr);
scTransparent, Workbook.AddColorToPalette(clr));
fmt.Background.BgColor := fmt.Background.FgColor; fmt.Background.BgColor := fmt.Background.FgColor;
if (fmt.Background.BgColor <> scTransparent) then if (fmt.Background.BgColor <> scTransparent) then
begin begin
@ -4444,8 +4432,6 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsSpreadOpenDocWriter.WriteBackgroundColorStyleXMLAsString( function TsSpreadOpenDocWriter.WriteBackgroundColorStyleXMLAsString(
const AFormat: TsCellFormat): String; const AFormat: TsCellFormat): String;
type
TRgb = record r,g,b,a: byte; end;
const // fraction of pattern color in fill pattern const // fraction of pattern color in fill pattern
FRACTION: array[TsFillStyle] of Double = ( FRACTION: array[TsFillStyle] of Double = (
0.0, 1.0, 0.75, 0.50, 0.25, 0.125, 0.0625, // fsNoFill..fsGray6 0.0, 1.0, 0.75, 0.50, 0.25, 0.125, 0.0625, // fsNoFill..fsGray6
@ -4453,8 +4439,8 @@ const // fraction of pattern color in fill pattern
0.25, 0.25, 0.25, 0.25, // fsThinStripeHor..fsThinStripeDiagDown 0.25, 0.25, 0.25, 0.25, // fsThinStripeHor..fsThinStripeDiagDown
0.5, 6.0/16, 0.75, 7.0/16); // fsHatchDiag..fsThinHatchHor 0.5, 6.0/16, 0.75, 7.0/16); // fsHatchDiag..fsThinHatchHor
var var
fc,bc: TsColorValue; fc,bc: TsColor;
mix: TRgb; mix: TRgba;
fraction_fc, fraction_bc: Double; fraction_fc, fraction_bc: Double;
begin begin
Result := ''; Result := '';
@ -4463,22 +4449,22 @@ begin
exit; exit;
// Foreground and background colors // Foreground and background colors
fc := Workbook.GetPaletteColor(AFormat.Background.FgColor); fc := AFormat.Background.FgColor;
if Aformat.Background.BgColor = scTransparent then if Aformat.Background.BgColor = scTransparent then
bc := Workbook.GetPaletteColor(scWhite) bc := scWhite
else else
bc := Workbook.GetPaletteColor(AFormat.Background.BgColor); bc := AFormat.Background.BgColor;
// Mixing fraction // Mixing fraction
fraction_fc := FRACTION[AFormat.Background.Style]; fraction_fc := FRACTION[AFormat.Background.Style];
fraction_bc := 1.0 - fraction_fc; fraction_bc := 1.0 - fraction_fc;
// Mixed color
mix.r := Min(round(fraction_fc*TRgb(fc).r + fraction_bc*TRgb(bc).r), 255);
mix.g := Min(round(fraction_fc*TRgb(fc).g + fraction_bc*TRgb(bc).g), 255);
mix.b := Min(round(fraction_fc*TRgb(fc).b + fraction_bc*TRgb(bc).b), 255);
Result := Format('fo:background-color="%s" ', [ // Mixed color
ColorToHTMLColorStr(TsColorValue(mix)) mix.r := Min(round(fraction_fc*TRgba(fc).r + fraction_bc*TRgba(bc).r), 255);
]); mix.g := Min(round(fraction_fc*TRgba(fc).g + fraction_bc*TRgba(bc).g), 255);
mix.b := Min(round(fraction_fc*TRgba(fc).b + fraction_bc*TRgba(bc).b), 255);
Result := Format('fo:background-color="%s" ', [ColorToHTMLColorStr(TsColor(mix))]);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -4499,7 +4485,7 @@ begin
Result := Result + Format('fo:border-bottom="%s %s %s" ', [ Result := Result + Format('fo:border-bottom="%s %s %s" ', [
BORDER_LINEWIDTHS[AFormat.BorderStyles[cbSouth].LineStyle], BORDER_LINEWIDTHS[AFormat.BorderStyles[cbSouth].LineStyle],
BORDER_LINESTYLES[AFormat.BorderStyles[cbSouth].LineStyle], BORDER_LINESTYLES[AFormat.BorderStyles[cbSouth].LineStyle],
Workbook.GetPaletteColorAsHTMLStr(AFormat.BorderStyles[cbSouth].Color) ColorToHTMLColorStr(AFormat.BorderStyles[cbSouth].Color)
]); ]);
if AFormat.BorderStyles[cbSouth].LineStyle = lsDouble then if AFormat.BorderStyles[cbSouth].LineStyle = lsDouble then
Result := Result + 'style:border-linewidth-bottom="0.002cm 0.035cm 0.002cm" '; Result := Result + 'style:border-linewidth-bottom="0.002cm 0.035cm 0.002cm" ';
@ -4512,7 +4498,7 @@ begin
Result := Result + Format('fo:border-left="%s %s %s" ', [ Result := Result + Format('fo:border-left="%s %s %s" ', [
BORDER_LINEWIDTHS[AFormat.BorderStyles[cbWest].LineStyle], BORDER_LINEWIDTHS[AFormat.BorderStyles[cbWest].LineStyle],
BORDER_LINESTYLES[AFormat.BorderStyles[cbWest].LineStyle], BORDER_LINESTYLES[AFormat.BorderStyles[cbWest].LineStyle],
Workbook.GetPaletteColorAsHTMLStr(AFormat.BorderStyles[cbWest].Color) ColorToHTMLColorStr(AFormat.BorderStyles[cbWest].Color)
]); ]);
if AFormat.BorderStyles[cbWest].LineStyle = lsDouble then if AFormat.BorderStyles[cbWest].LineStyle = lsDouble then
Result := Result + 'style:border-linewidth-left="0.002cm 0.035cm 0.002cm" '; Result := Result + 'style:border-linewidth-left="0.002cm 0.035cm 0.002cm" ';
@ -4525,7 +4511,7 @@ begin
Result := Result + Format('fo:border-right="%s %s %s" ', [ Result := Result + Format('fo:border-right="%s %s %s" ', [
BORDER_LINEWIDTHS[AFormat.BorderStyles[cbEast].LineStyle], BORDER_LINEWIDTHS[AFormat.BorderStyles[cbEast].LineStyle],
BORDER_LINESTYLES[AFormat.BorderStyles[cbEast].LineStyle], BORDER_LINESTYLES[AFormat.BorderStyles[cbEast].LineStyle],
Workbook.GetPaletteColorAsHTMLStr(AFormat.BorderStyles[cbEast].Color) ColorToHTMLColorStr(AFormat.BorderStyles[cbEast].Color)
]); ]);
if AFormat.BorderStyles[cbSouth].LineStyle = lsDouble then if AFormat.BorderStyles[cbSouth].LineStyle = lsDouble then
Result := Result + 'style:border-linewidth-right="0.002cm 0.035cm 0.002cm" '; Result := Result + 'style:border-linewidth-right="0.002cm 0.035cm 0.002cm" ';
@ -4538,7 +4524,7 @@ begin
Result := Result + Format('fo:border-top="%s %s %s" ', [ Result := Result + Format('fo:border-top="%s %s %s" ', [
BORDER_LINEWIDTHS[AFormat.BorderStyles[cbNorth].LineStyle], BORDER_LINEWIDTHS[AFormat.BorderStyles[cbNorth].LineStyle],
BORDER_LINESTYLES[AFormat.BorderStyles[cbNorth].LineStyle], BORDER_LINESTYLES[AFormat.BorderStyles[cbNorth].LineStyle],
Workbook.GetPaletteColorAsHTMLStr(AFormat.BorderStyles[cbNorth].Color) ColorToHTMLColorStr(AFormat.BorderStyles[cbNorth].Color)
]); ]);
if AFormat.BorderStyles[cbSouth].LineStyle = lsDouble then if AFormat.BorderStyles[cbSouth].LineStyle = lsDouble then
Result := Result + 'style:border-linewidth-top="0.002cm 0.035cm 0.002cm" '; Result := Result + 'style:border-linewidth-top="0.002cm 0.035cm 0.002cm" ';
@ -4550,7 +4536,7 @@ begin
Result := Result + Format('style:diagonal-bl-tr="%s %s %s" ', [ Result := Result + Format('style:diagonal-bl-tr="%s %s %s" ', [
BORDER_LINEWIDTHS[AFormat.BorderStyles[cbDiagUp].LineStyle], BORDER_LINEWIDTHS[AFormat.BorderStyles[cbDiagUp].LineStyle],
BORDER_LINESTYLES[AFormat.BorderStyles[cbDiagUp].LineStyle], BORDER_LINESTYLES[AFormat.BorderStyles[cbDiagUp].LineStyle],
Workbook.GetPaletteColorAsHTMLStr(AFormat.BorderStyles[cbDiagUp].Color) ColorToHTMLColorStr(AFormat.BorderStyles[cbDiagUp].Color)
]); ]);
end; end;
@ -4559,7 +4545,7 @@ begin
Result := Result + Format('style:diagonal-tl-br="%s %s %s" ', [ Result := Result + Format('style:diagonal-tl-br="%s %s %s" ', [
BORDER_LINEWIDTHS[AFormat.BorderStyles[cbDiagDown].LineStyle], BORDER_LINEWIDTHS[AFormat.BorderStyles[cbDiagDown].LineStyle],
BORDER_LINESTYLES[AFormat.BorderStyles[cbDiagDown].LineStyle], BORDER_LINESTYLES[AFormat.BorderStyles[cbDiagDown].LineStyle],
Workbook.GetPaletteColorAsHTMLStr(AFormat.BorderStyles[cbDiagDown].Color) ColorToHTMLColorStr(AFormat.BorderStyles[cbDiagDown].Color)
]); ]);
end; end;
end; end;
@ -4613,7 +4599,7 @@ begin
Result := Result + 'style:text-line-through-style="solid" '; Result := Result + 'style:text-line-through-style="solid" ';
if AFont.Color <> defFnt.Color then if AFont.Color <> defFnt.Color then
Result := Result + Format('fo:color="%s" ', [Workbook.GetPaletteColorAsHTMLStr(AFont.Color)]); Result := Result + Format('fo:color="%s" ', [ColorToHTMLColorStr(AFont.Color)]);
end; end;
function TsSpreadOpenDocWriter.WriteFontStyleXMLAsString( function TsSpreadOpenDocWriter.WriteFontStyleXMLAsString(

View File

@ -0,0 +1,414 @@
{ fpsPalette }
{@@ ----------------------------------------------------------------------------
Palette support for fpspreadsheet file formats
AUTHORS: Werner Pamler, Felipe Monteiro de Carvalho, Reinier Olislagers
LICENSE: See the file COPYING.modifiedLGPL.txt, included in the Lazarus
distribution, for details about the license.
-------------------------------------------------------------------------------}
unit fpsPalette;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fpstypes, fpspreadsheet;
type
{ TsPalette }
TsPalette = class
private
FColors: array of TsColor;
function GetColor(AIndex: Integer): TsColor;
procedure SetColor(AIndex: Integer; AColor: TsColor);
public
constructor Create;
procedure AddBuiltinColors; virtual;
function AddColor(AColor: TsColor; ABigEndian: Boolean = false): Integer;
procedure AddExcelColors;
function AddUniqueColor(AColor: TsColor; ABigEndian: Boolean = false): Integer;
procedure Clear;
procedure CollectFromWorkbook(AWorkbook: TsWorkbook);
function ColorUsedInWorkbook(APaletteIndex: Integer; AWorkbook: TsWorkbook): Boolean;
function FindClosestColorIndex(AColor: TsColor; AMaxPaletteCount: Integer = -1): Integer;
function FindColor(AColor: TsColor; AMaxPaletteCount: Integer = -1): Integer;
function Count: Integer;
procedure Trim(AMaxSize: Integer);
procedure UseColors(const AColors: array of TsColor; ABigEndian: Boolean = false);
property Colors[AIndex: Integer]: TsColor read GetColor write SetColor; default;
end;
procedure MakeLEPalette(var AColors: array of TsColor);
implementation
uses
fpsutils;
{@@ ----------------------------------------------------------------------------
If a palette is coded as big-endian (e.g. by copying the rgb values from
the OpenOffice documentation) the palette values can be converted by means
of this procedure to little-endian which is required by fpspreadsheet.
@param AColors Color array to be converted.
After conversion, its color values are replaced.
-------------------------------------------------------------------------------}
procedure MakeLEPalette(var AColors: array of TsColor);
var
i: Integer;
begin
for i := 0 to High(AColors) do
AColors[i] := LongRGBToExcelPhysical(AColors[i])
end;
{@@ ----------------------------------------------------------------------------
Constructor of the palette: initializes the color array
-------------------------------------------------------------------------------}
constructor TsPalette.Create;
begin
inherited;
SetLength(FColors, 0);
end;
{@@ ----------------------------------------------------------------------------
Adds an rgb color value to the palette and returns the palette index
of the new color.
Existing colors are not checked.
If ABigEndian is TRUE then the rgb values are assumed to be in big endian
order (r = high byte).
By default, rgb is in little-endian order (r = low byte)
-------------------------------------------------------------------------------}
function TsPalette.AddColor(AColor: TsColor; ABigEndian: Boolean = false): Integer;
begin
if ABigEndian then
AColor := LongRGBToExcelPhysical(AColor);
SetLength(FColors, Length(FColors) + 1);
FColors[High(FColors)] := AColor;
end;
{@@ ----------------------------------------------------------------------------
Adds the built-in colors
-------------------------------------------------------------------------------}
procedure TsPalette.AddBuiltinColors;
begin
AddColor(scBlack); // 0
AddColor(scWhite); // 1
AddColor(scRed); // 2
AddColor(scGreen); // 3
AddColor(scBlue); // 4
AddColor(scYellow); // 5
AddColor(scMagenta); // 6
AddColor(scCyan); // 7
end;
{@@ ----------------------------------------------------------------------------
Adds the standard palette of Excel 8
NOTE: To get the full Excel8 palette call this after AddBuiltinColors
-------------------------------------------------------------------------------}
procedure TsPalette.AddExcelColors;
begin
AddColor($000000, true); // $08: EGA black
AddColor($FFFFFF, true); // $09: EGA white
AddColor($FF0000, true); // $0A: EGA red
AddColor($00FF00, true); // $0B: EGA green
AddColor($0000FF, true); // $0C: EGA blue
AddColor($FFFF00, true); // $0D: EGA yellow
AddColor($FF00FF, true); // $0E: EGA magenta
AddColor($00FFFF, true); // $0F: EGA cyan
AddColor($800000, true); // $10: EGA dark red
AddColor($008000, true); // $11: EGA dark green
AddColor($000080, true); // $12: EGA dark blue
AddColor($808000, true); // $13: EGA olive
AddColor($800080, true); // $14: EGA purple
AddColor($008080, true); // $15: EGA teal
AddColor($C0C0C0, true); // $16: EGA silver
AddColor($808080, true); // $17: EGA gray
AddColor($9999FF, true); // $18:
AddColor($993366, true); // $19:
AddColor($FFFFCC, true); // $1A:
AddColor($CCFFFF, true); // $1B:
AddColor($660066, true); // $1C:
AddColor($FF8080, true); // $1D:
AddColor($0066CC, true); // $1E:
AddColor($CCCCFF, true); // $1F:
AddColor($000080, true); // $20:
AddColor($FF00FF, true); // $21:
AddColor($FFFF00, true); // $22:
AddColor($00FFFF, true); // $23:
AddColor($800080, true); // $24:
AddColor($800000, true); // $25:
AddColor($008080, true); // $26:
AddColor($0000FF, true); // $27:
AddColor($00CCFF, true); // $28:
AddColor($CCFFFF, true); // $29:
AddColor($CCFFCC, true); // $2A:
AddColor($FFFF99, true); // $2B:
AddColor($99CCFF, true); // $2C:
AddColor($FF99CC, true); // $2D:
AddColor($CC99FF, true); // $2E:
AddColor($FFCC99, true); // $2F:
AddColor($3366FF, true); // $30:
AddColor($33CCCC, true); // $31:
AddColor($99CC00, true); // $32:
AddColor($FFCC00, true); // $33:
AddColor($FF9900, true); // $34:
AddColor($FF6600, true); // $35:
AddColor($666699, true); // $36:
AddColor($969696, true); // $37:
AddColor($003366, true); // $38:
AddColor($339966, true); // $39:
AddColor($003300, true); // $3A:
AddColor($333300, true); // $3B:
AddColor($993300, true); // $3C:
AddColor($993366, true); // $3D:
AddColor($333399, true); // $3E:
AddColor($333333, true); // $3F:
end;
{@@ ----------------------------------------------------------------------------
Adds the specified color to the palette if it does not yet exist.
Returns the palette index of the new or existing color
-------------------------------------------------------------------------------}
function TsPalette.AddUniqueColor(AColor: TsColor;
ABigEndian: Boolean = false): Integer;
begin
if ABigEndian then
AColor := LongRGBToExcelPhysical(AColor);
Result := FindColor(AColor);
if Result = -1 then result := AddColor(AColor);
end;
{@@ ----------------------------------------------------------------------------
Clears the palette
-------------------------------------------------------------------------------}
procedure TsPalette.Clear;
begin
SetLength(FColors, 0);
end;
{@@ ----------------------------------------------------------------------------
Collects the colors used in the specified workbook
-------------------------------------------------------------------------------}
procedure TsPalette.CollectFromWorkbook(AWorkbook: TsWorkbook);
var
i: Integer;
sheet: TsWorksheet;
cell: PCell;
fmt: TsCellFormat;
fnt: TsFont;
cb: TsCellBorder;
begin
for i:=0 to AWorkbook.GetWorksheetCount-1 do
begin
sheet := AWorkbook.GetWorksheetByIndex(i);
for cell in sheet.Cells do begin
fmt := sheet.ReadCellFormat(cell);
if (uffBackground in fmt.UsedFormattingFields) then
begin
AddUniqueColor(fmt.Background.BgColor);
AddUniqueColor(fmt.Background.FgColor);
end;
if (uffFont in fmt.UsedFormattingFields) then
begin
fnt := AWorkbook.GetFont(fmt.FontIndex);
AddUniqueColor(fnt.Color);
end;
if (uffBorder in fmt.UsedFormattingFields) then
begin
for cb in TsCellBorder do
if (cb in fmt.Border) then
AddUniqueColor(fmt.BorderStyles[cb].Color);
end;
end;
end;
end;
{@@ ----------------------------------------------------------------------------
Checks whether a given color is used somewhere within the entire workbook
@param APaletteIndex Palette index of the color
@result True if the color is used by at least one cell, false if not.
-------------------------------------------------------------------------------}
function TsPalette.ColorUsedInWorkbook(APaletteIndex: Integer;
AWorkbook: TsWorkbook): Boolean;
var
sheet: TsWorksheet;
cell: PCell;
i: Integer;
fnt: TsFont;
b: TsCellBorder;
fmt: PsCellFormat;
color: TsColor;
begin
color := GetColor(APaletteIndex);
if (color = scNotDefined) or (AWorkbook = nil) then
exit(false);
Result := true;
for i:=0 to AWorkbook.GetWorksheetCount-1 do
begin
sheet := AWorkbook.GetWorksheetByIndex(i);
for cell in sheet.Cells do
begin
fmt := AWorkbook.GetPointerToCellFormat(cell^.FormatIndex);
if (uffBackground in fmt^.UsedFormattingFields) then
begin
if fmt^.Background.BgColor = color then exit;
if fmt^.Background.FgColor = color then exit;
end;
if (uffBorder in fmt^.UsedFormattingFields) then
for b in TsCellBorders do
if (b in fmt^.Border) and (fmt^.BorderStyles[b].Color = color) then
exit;
if (uffFont in fmt^.UsedFormattingFields) then
begin
fnt := AWorkbook.GetFont(fmt^.FontIndex);
if fnt.Color = color then
exit;
end;
end;
end;
Result := false;
end;
{@@ ----------------------------------------------------------------------------
Finds the palette color index which points to a color that is "closest" to a
given color. "Close" means here smallest length of the rgb-difference vector.
@param AColor Rgb color value to be considered
@param AMaxPaletteCount Number of palette entries considered. Example:
BIFF5/BIFF8 can write only 64 colors, i.e
AMaxPaletteCount = 64
@return Palette index of the color closest to AColor
-------------------------------------------------------------------------------}
function TsPalette.FindClosestColorIndex(AColor: TsColor;
AMaxPaletteCount: Integer = -1): Integer;
type
TRGBA = record r,g,b,a: Byte end;
var
rgb: TRGBA;
rgb0: TRGBA absolute AColor;
dist: Double;
minDist: Double;
i: Integer;
n: Integer;
begin
Result := -1;
minDist := 1E108;
n := Length(FColors);
if AMaxPaletteCount > n then n := AMaxPaletteCount;
for i := 0 to n - 1 do
begin
rgb := TRGBA(GetColor(i));
dist := sqr(rgb.r - rgb0.r) + sqr(rgb.g - rgb0.g) + sqr(rgb.b - rgb0.b);
if dist < minDist then
begin
Result := i;
minDist := dist;
end;
end;
end;
{@@ ----------------------------------------------------------------------------
Finds the palette color index which belongs to the specified color.
Returns -1 if the color is not contained in the palette.
@param AColor Rgb color value to be considered
@param AMaxPaletteCount Number of palette entries considered. Example:
BIFF5/BIFF8 can write only 64 colors, i.e
AMaxPaletteCount = 64
@return Palette index of AColor
-------------------------------------------------------------------------------}
function TsPalette.FindColor(AColor: TsColor;
AMaxPaletteCount: Integer = -1): Integer;
var
n: Integer;
begin
n := Length(FColors);
if AMaxPaletteCount > n then n := AMaxPaletteCount;
for Result := 0 to n - 1 do
if GetColor(Result) = AColor then
exit;
Result := -1;
end;
{@@ ----------------------------------------------------------------------------
Reads the rgb color for the given index from the palette.
Can be type-cast to TColor for usage in GUI applications.
@param AIndex Index of the color considered
@return A number containing the rgb components in little-endian notation.
-------------------------------------------------------------------------------}
function TsPalette.GetColor(AIndex: Integer): TsColor;
begin
if (AIndex >= 0) and (AIndex < Length(FColors)) then
Result := FColors[AIndex]
else
Result := scNotDefined;
end;
{@@ ----------------------------------------------------------------------------
Returns the number of palette colors
-------------------------------------------------------------------------------}
function TsPalette.Count: Integer;
begin
Result := Length(FColors);
end;
{@@ ----------------------------------------------------------------------------
Replaces a color value of the palette by a new value.
The color must be given in little-endian notation (ABGR, with A=0).
@param AIndex Palette index of the color to be replaced
@param AColor Number containing the rgb components of the new color
-------------------------------------------------------------------------------}
procedure TsPalette.SetColor(AIndex: Integer; AColor: TsColor);
begin
if (AIndex >= 0) and (AIndex < Length(FColors)) then
FColors[AIndex] := AColor;
end;
{@@ ----------------------------------------------------------------------------
Trims the size of the palette
-------------------------------------------------------------------------------}
procedure TsPalette.Trim(AMaxSize: Integer);
begin
if Length(FColors) > AMaxSize then
SetLength(FColors, AMaxSize);
end;
{@@ ----------------------------------------------------------------------------
Uses the color array to with "APalette" points in the palette.
If ABigEndian is true it is assumed that the input colors are specified in
big-endian notation, i.e. "blue" in the low-value byte.
-------------------------------------------------------------------------------}
procedure TsPalette.UseColors(const AColors: array of TsColor; ABigEndian: Boolean = false);
var
i: Integer;
begin
SetLength(FColors, High(AColors)+1);
if ABigEndian then
for i:=0 to High(AColors) do FColors[i] := LongRGBToExcelPhysical(AColors[i])
else
for i:=0 to High(AColors) do FColors[i] := AColors[i];
end;
end.

View File

@ -549,7 +549,7 @@ type
FWorksheets: TFPList; FWorksheets: TFPList;
FFormat: TsSpreadsheetFormat; FFormat: TsSpreadsheetFormat;
FBuiltinFontCount: Integer; FBuiltinFontCount: Integer;
FPalette: array of TsColorValue; //FPalette: array of TsColorValue;
FVirtualColCount: Cardinal; FVirtualColCount: Cardinal;
FVirtualRowCount: Cardinal; FVirtualRowCount: Cardinal;
FReadWriteFlag: TsReadWriteFlag; FReadWriteFlag: TsReadWriteFlag;
@ -565,7 +565,7 @@ type
FOnRemoveWorksheet: TsRemoveWorksheetEvent; FOnRemoveWorksheet: TsRemoveWorksheetEvent;
FOnRemovingWorksheet: TsWorksheetEvent; FOnRemovingWorksheet: TsWorksheetEvent;
FOnSelectWorksheet: TsWorksheetEvent; FOnSelectWorksheet: TsWorksheetEvent;
FOnChangePalette: TNotifyEvent; // FOnChangePalette: TNotifyEvent;
FFileName: String; FFileName: String;
FLockCount: Integer; FLockCount: Integer;
FLog: TStringList; FLog: TStringList;
@ -668,11 +668,8 @@ type
function AddNumberFormat(AFormatStr: String): Integer; function AddNumberFormat(AFormatStr: String): Integer;
function GetNumberFormat(AIndex: Integer): TsNumFormatParams; function GetNumberFormat(AIndex: Integer): TsNumFormatParams;
function GetNumberFormatCount: Integer; function GetNumberFormatCount: Integer;
(*
{ Color handling } { Color handling }
function AddColorToPalette(AColorValue: TsColorValue): TsColor;
function FindClosestColor(AColorValue: TsColorValue;
AMaxPaletteCount: Integer = -1): TsColor;
function FPSColorToHexString(AColor: TsColor; ARGBColor: TFPColor): String; function FPSColorToHexString(AColor: TsColor; ARGBColor: TFPColor): String;
function GetColorName(AColorIndex: TsColor): string; overload; function GetColorName(AColorIndex: TsColor): string; overload;
procedure GetColorName(AColorValue: TsColorValue; out AName: String); overload; procedure GetColorName(AColorValue: TsColorValue; out AName: String); overload;
@ -684,6 +681,7 @@ type
procedure UsePalette(APalette: PsPalette; APaletteCount: Word; procedure UsePalette(APalette: PsPalette; APaletteCount: Word;
ABigEndian: Boolean = false); ABigEndian: Boolean = false);
function UsesColor(AColorIndex: TsColor): Boolean; function UsesColor(AColorIndex: TsColor): Boolean;
*)
{ Utilities } { Utilities }
procedure UpdateCaches; procedure UpdateCaches;
@ -708,7 +706,7 @@ type
{@@ This event fires whenever a new worksheet is added } {@@ This event fires whenever a new worksheet is added }
property OnAddWorksheet: TsWorksheetEvent read FOnAddWorksheet write FOnAddWorksheet; property OnAddWorksheet: TsWorksheetEvent read FOnAddWorksheet write FOnAddWorksheet;
{@@ This event fires whenever the workbook palette changes. } {@@ This event fires whenever the workbook palette changes. }
property OnChangePalette: TNotifyEvent read FOnChangePalette write FOnChangePalette; // property OnChangePalette: TNotifyEvent read FOnChangePalette write FOnChangePalette;
{@@ This event fires whenever a worksheet is changed } {@@ This event fires whenever a worksheet is changed }
property OnChangeWorksheet: TsWorksheetEvent read FOnChangeWorksheet write FOnChangeWorksheet; property OnChangeWorksheet: TsWorksheetEvent read FOnChangeWorksheet write FOnChangeWorksheet;
{@@ This event fires whenever a workbook is loaded } {@@ This event fires whenever a workbook is loaded }
@ -778,7 +776,6 @@ type
procedure CopyCellFormat(AFromCell, AToCell: PCell); procedure CopyCellFormat(AFromCell, AToCell: PCell);
procedure CopyCellValue(AFromCell, AToCell: PCell); procedure CopyCellValue(AFromCell, AToCell: PCell);
procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer);
//function SameCellBorders(ACell1, ACell2: PCell): Boolean; overload; //function SameCellBorders(ACell1, ACell2: PCell): Boolean; overload;
function SameCellBorders(AFormat1, AFormat2: PsCellFormat): Boolean; //overload; function SameCellBorders(AFormat1, AFormat2: PsCellFormat): Boolean; //overload;
@ -826,7 +823,7 @@ const
DEF_CHART_NEUTRAL_COLORVALUE = $FFFFFF; DEF_CHART_NEUTRAL_COLORVALUE = $FFFFFF;
DEF_TOOLTIP_TEXT_COLORVALUE = $000000; DEF_TOOLTIP_TEXT_COLORVALUE = $000000;
DEF_FONT_AUTOMATIC_COLORVALUE = $000000; DEF_FONT_AUTOMATIC_COLORVALUE = $000000;
(*
var var
{@@ RGB colors RGB in "big-endian" notation (red at left). The values are inverted {@@ RGB colors RGB in "big-endian" notation (red at left). The values are inverted
at initialization to be little-endian at run-time! at initialization to be little-endian at run-time!
@ -883,26 +880,7 @@ var
'beige', // $15 'beige', // $15
'wheat' // $16 'wheat' // $16
); );
*)
{@@ ----------------------------------------------------------------------------
If a palette is coded as big-endian (e.g. by copying the rgb values from
the OpenOffice documentation) the palette values can be converted by means
of this procedure to little-endian which is required internally by TsWorkbook.
@param APalette Pointer to the palette to be converted. After conversion,
its color values are replaced.
@param APaletteSize Number of colors contained in the palette
-------------------------------------------------------------------------------}
procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer);
var
i: Integer;
begin
{$PUSH}{$R-}
for i := 0 to APaletteSize-1 do
APalette^[i] := LongRGBToExcelPhysical(APalette^[i])
{$POP}
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Copies the format of a cell to another one. Copies the format of a cell to another one.
@ -916,7 +894,6 @@ var
numFmtParams: TsNumFormatParams; numFmtParams: TsNumFormatParams;
nfs: String; nfs: String;
font: TsFont; font: TsFont;
clr: TsColorvalue;
cb: TsCellBorder; cb: TsCellBorder;
begin begin
Assert(AFromCell <> nil); Assert(AFromCell <> nil);
@ -929,6 +906,7 @@ begin
begin begin
fmt := sourceSheet.ReadCellFormat(AFromCell); fmt := sourceSheet.ReadCellFormat(AFromCell);
//destSheet.WriteCellFormat(AToCell, fmt); //destSheet.WriteCellFormat(AToCell, fmt);
{
if (uffBackground in fmt.UsedFormattingFields) then if (uffBackground in fmt.UsedFormattingFields) then
begin begin
clr := sourceSheet.Workbook.GetPaletteColor(fmt.Background.BgColor); clr := sourceSheet.Workbook.GetPaletteColor(fmt.Background.BgColor);
@ -936,21 +914,26 @@ begin
clr := sourceSheet.Workbook.GetPaletteColor(fmt.Background.FgColor); clr := sourceSheet.Workbook.GetPaletteColor(fmt.Background.FgColor);
fmt.Background.FgColor := destSheet.Workbook.AddColorToPalette(clr); fmt.Background.FgColor := destSheet.Workbook.AddColorToPalette(clr);
end; end;
}
if (uffFont in fmt.UsedFormattingFields) then if (uffFont in fmt.UsedFormattingFields) then
begin begin
font := sourceSheet.ReadCellFont(AFromCell); font := sourceSheet.ReadCellFont(AFromCell);
{
clr := sourceSheet.Workbook.GetPaletteColor(font.Color); clr := sourceSheet.Workbook.GetPaletteColor(font.Color);
font.Color := destSheet.Workbook.AddColorToPalette(clr); font.Color := destSheet.Workbook.AddColorToPalette(clr);
}
fmt.FontIndex := destSheet.Workbook.FindFont(font.FontName, font.Size, font.Style, font.Color); fmt.FontIndex := destSheet.Workbook.FindFont(font.FontName, font.Size, font.Style, font.Color);
if fmt.FontIndex = -1 then if fmt.FontIndex = -1 then
fmt.FontIndex := destSheet.Workbook.AddFont(font.FontName, font.Size, font.Style, font.Color); fmt.FontIndex := destSheet.Workbook.AddFont(font.FontName, font.Size, font.Style, font.Color);
end; end;
{
if (uffBorder in fmt.UsedFormattingFields) then if (uffBorder in fmt.UsedFormattingFields) then
for cb in fmt.Border do for cb in fmt.Border do
begin begin
clr := sourceSheet.Workbook.GetPaletteColor(fmt.BorderStyles[cb].Color); clr := sourceSheet.Workbook.GetPaletteColor(fmt.BorderStyles[cb].Color);
fmt.BorderStyles[cb].Color := destSheet.Workbook.AddColorToPalette(clr); fmt.BorderStyles[cb].Color := destSheet.Workbook.AddColorToPalette(clr);
end; end;
}
if (uffNumberformat in fmt.UsedFormattingFields) then if (uffNumberformat in fmt.UsedFormattingFields) then
begin begin
numFmtParams := sourceSheet.Workbook.GetNumberFormat(fmt.NumberFormatIndex); numFmtParams := sourceSheet.Workbook.GetNumberFormat(fmt.NumberFormatIndex);
@ -1086,7 +1069,8 @@ begin
IfThen(fssItalic in fnt.Style, 'i', '.'), IfThen(fssItalic in fnt.Style, 'i', '.'),
IfThen(fssUnderline in fnt.Style, 'u', '.'), IfThen(fssUnderline in fnt.Style, 'u', '.'),
IfThen(fssStrikeOut in fnt.Style, 's', '.'), IfThen(fssStrikeOut in fnt.Style, 's', '.'),
AWorkbook.GetPaletteColorAsHTMLStr(fnt.Color) ColorToHTMLColorStr(fnt.Color)
//AWorkbook.GetPaletteColorAsHTMLStr(fnt.Color)
])); ]));
end; end;
L.SaveToFile(AFileName); L.SaveToFile(AFileName);
@ -2847,10 +2831,10 @@ begin
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Returns the background color of a cell as index into the workbook's color palette. Returns the background color of a cell as rbg value
@param ACell Pointer to the cell @param ACell Pointer to the cell
@return Index of the cell background color into the workbook's color palette @return Value containing the rgb bytes in little-endian order
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.ReadBackgroundColor(ACell: PCell): TsColor; function TsWorksheet.ReadBackgroundColor(ACell: PCell): TsColor;
var var
@ -4857,8 +4841,7 @@ end;
@param ARow The row of the cell @param ARow The row of the cell
@param ACol The column of the cell @param ACol The column of the cell
@param AFontColor Index into the workbook's color palette identifying the @param AFontColor RGB value of the new text color
new text color.
@return Index of the font in the workbook's font list. @return Index of the font in the workbook's font list.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer; function TsWorksheet.WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer;
@ -4872,8 +4855,7 @@ end;
is created. Returns the index of this font in the font list. is created. Returns the index of this font in the font list.
@param ACell Pointer to the cell @param ACell Pointer to the cell
@param AFontColor Index into the workbook's color palette identifying the @param AFontColor RGB value of the new text color
new text color.
@return Index of the font in the workbook's font list. @return Index of the font in the workbook's font list.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.WriteFontColor(ACell: PCell; AFontColor: TsColor): Integer; function TsWorksheet.WriteFontColor(ACell: PCell; AFontColor: TsColor): Integer;
@ -5093,8 +5075,8 @@ end;
@param ARow Row index of the cell @param ARow Row index of the cell
@param ACol Column index of the cell @param ACol Column index of the cell
@param AFillStyle Fill style to be used - see TsFillStyle @param AFillStyle Fill style to be used - see TsFillStyle
@param APatternColor Palette index of the pattern color @param APatternColor RGB value of the pattern color
@param ABackgroundColor Palette index of the background color @param ABackgroundColor RGB value of the background color
@return Pointer to cell @return Pointer to cell
@NOTE Is replaced by uniform fill if WriteBackgroundColor is called later. @NOTE Is replaced by uniform fill if WriteBackgroundColor is called later.
@ -5111,8 +5093,8 @@ end;
@param ACell Pointer to the cell @param ACell Pointer to the cell
@param AStyle Fill style ("pattern") to be used - see TsFillStyle @param AStyle Fill style ("pattern") to be used - see TsFillStyle
@param APatternColor Palette index of the pattern color @param APatternColor RGB value of the pattern color
@param ABackgroundColor Palette index of the background color @param ABackgroundColor RGB value of the background color
@NOTE Is replaced by uniform fill if WriteBackgroundColor is called later. @NOTE Is replaced by uniform fill if WriteBackgroundColor is called later.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -5147,9 +5129,9 @@ end;
@param ARow Row index of the cell @param ARow Row index of the cell
@param ACol Column index of the cell @param ACol Column index of the cell
@param AColor Index of the new background color into the workbook's @param AColor RGB value of the new background color.
color palette. Use the color index scTransparent to Use the value "scTransparent" to clear an existing
erase an existing background color. background color.
@return Pointer to cell @return Pointer to cell
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.WriteBackgroundColor(ARow, ACol: Cardinal; function TsWorksheet.WriteBackgroundColor(ARow, ACol: Cardinal;
@ -5163,9 +5145,9 @@ end;
Sets a uniform background color of a cell. Sets a uniform background color of a cell.
@param ACell Pointer to cell @param ACell Pointer to cell
@param AColor Index of the new background color into the workbook's @param AColor RGB value of the new background color.
color palette. Use the color index scTransparent to Use the value "scTransparent" to clear an existing
erase an existing background color. background color.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorksheet.WriteBackgroundColor(ACell: PCell; AColor: TsColor); procedure TsWorksheet.WriteBackgroundColor(ACell: PCell; AColor: TsColor);
begin begin
@ -5185,8 +5167,7 @@ end;
@param ACol Column index of the cell @param ACol Column index of the cell
@param ABorder Indicates to which border (left/top etc) this color is @param ABorder Indicates to which border (left/top etc) this color is
to be applied to be applied
@param AColor Index of the new border color into the workbook's @param AColor RGB value of the new border color
color palette.
@return Pointer to cell @return Pointer to cell
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.WriteBorderColor(ARow, ACol: Cardinal; function TsWorksheet.WriteBorderColor(ARow, ACol: Cardinal;
@ -5203,8 +5184,7 @@ end;
@param ACell Pointer to cell @param ACell Pointer to cell
@param ABorder Indicates to which border (left/top etc) this color is @param ABorder Indicates to which border (left/top etc) this color is
to be applied to be applied
@param AColor Index of the new border color into the workbook's @param AColor RGB value of the new border color
color palette.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorksheet.WriteBorderColor(ACell: PCell; ABorder: TsCellBorder; procedure TsWorksheet.WriteBorderColor(ACell: PCell; ABorder: TsCellBorder;
AColor: TsColor); AColor: TsColor);
@ -5355,7 +5335,7 @@ end;
@param ACol Column index of the considered cell @param ACol Column index of the considered cell
@param ABorder Identifier of the border to be modified @param ABorder Identifier of the border to be modified
@param ALineStyle Identifier for the new line style of the border @param ALineStyle Identifier for the new line style of the border
@param AColor Palette index for the color of the border line @param AColor RGB value of the border line color
@return Pointer to cell @return Pointer to cell
@see WriteBorderStyles @see WriteBorderStyles
@ -5374,7 +5354,7 @@ end;
@param ACell Pointer to cell @param ACell Pointer to cell
@param ABorder Identifier of the border to be modified @param ABorder Identifier of the border to be modified
@param ALineStyle Identifier for the new line style of the border @param ALineStyle Identifier for the new line style of the border
@param AColor Palette index for the color of the border line @param AColor RGB value of the color of the border line
@see WriteBorderStyles @see WriteBorderStyles
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -6302,8 +6282,6 @@ begin
FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat); FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat);
FormatSettings.LongDateFormat := MakeLongDateFormat(FormatSettings.ShortDateFormat); FormatSettings.LongDateFormat := MakeLongDateFormat(FormatSettings.ShortDateFormat);
UseDefaultPalette;
FFontList := TFPList.Create; FFontList := TFPList.Create;
SetDefaultFont(DEFAULT_FONTNAME, DEFAULT_FONTSIZE); SetDefaultFont(DEFAULT_FONTNAME, DEFAULT_FONTSIZE);
InitFonts; InitFonts;
@ -7249,7 +7227,7 @@ end;
@param AFontName Name of the font (like 'Arial') @param AFontName Name of the font (like 'Arial')
@param ASize Size of the font in points @param ASize Size of the font in points
@param AStyle Style of the font, a combination of TsFontStyle elements @param AStyle Style of the font, a combination of TsFontStyle elements
@param AColor Color of the font, given by its index into the workbook's palette. @param AColor RGB valoe of the font color
@return Index of the font in the workbook's font list @return Index of the font in the workbook's font list
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorkbook.AddFont(const AFontName: String; ASize: Single; function TsWorkbook.AddFont(const AFontName: String; ASize: Single;
@ -7301,11 +7279,13 @@ end;
@param AFontName Name of the font (like 'Arial') @param AFontName Name of the font (like 'Arial')
@param ASize Size of the font in points @param ASize Size of the font in points
@param AStyle Style of the font, a combination of TsFontStyle elements @param AStyle Style of the font, a combination of TsFontStyle elements
@param AColor Color of the font, given by its index into the workbook's palette. @param AColor RGB value of the font color
@return Index of the font in the font list, or -1 if not found. @return Index of the font in the font list, or -1 if not found.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorkbook.FindFont(const AFontName: String; ASize: Single; function TsWorkbook.FindFont(const AFontName: String; ASize: Single;
AStyle: TsFontStyles; AColor: TsColor): Integer; AStyle: TsFontStyles; AColor: TsColor): Integer;
const
EPS = 1e-3;
var var
fnt: TsFont; fnt: TsFont;
begin begin
@ -7314,9 +7294,9 @@ begin
fnt := TsFont(FFontList.Items[Result]); fnt := TsFont(FFontList.Items[Result]);
if (fnt <> nil) and if (fnt <> nil) and
SameText(AFontName, fnt.FontName) and SameText(AFontName, fnt.FontName) and
(abs(ASize - fnt.Size) < 0.001) and // careful when comparing floating point numbers SameValue(ASize, fnt.Size, EPS) and // careful when comparing floating point numbers
(AStyle = fnt.Style) and (AStyle = fnt.Style) and
(AColor = fnt.Color) // Take care of limited palette size! (AColor = fnt.Color)
then then
exit; exit;
end; end;
@ -7520,7 +7500,7 @@ function TsWorkbook.GetNumberFormatCount: Integer;
begin begin
Result := FNumFormatList.Count; Result := FNumFormatList.Count;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Adds a color to the palette and returns its palette index, but only if the Adds a color to the palette and returns its palette index, but only if the
color does not already exist - in this case, it returns the index of the color does not already exist - in this case, it returns the index of the
@ -7602,7 +7582,7 @@ begin
if Assigned(FOnChangePalette) then FOnChangePalette(self); if Assigned(FOnChangePalette) then FOnChangePalette(self);
end; end;
*)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Adds a (simple) error message to an internal list Adds a (simple) error message to an internal list
@ -7639,47 +7619,7 @@ function TsWorkbook.GetErrorMsg: String;
begin begin
Result := FLog.Text; Result := FLog.Text;
end; end;
(*
{@@ ----------------------------------------------------------------------------
Finds the palette color index which points to a color that is closest to a
given color. "Close" means here smallest length of the rgb-difference vector.
@param AColorValue Rgb color value to be considered
@param AMaxPaletteCount Number of palette entries considered. Example:
BIFF5/BIFF8 can write only 64 colors, i.e
AMaxPaletteCount = 64
@return Palette index of the color closest to AColorValue
-------------------------------------------------------------------------------}
function TsWorkbook.FindClosestColor(AColorValue: TsColorValue;
AMaxPaletteCount: Integer = -1): TsColor;
type
TRGBA = record r,g,b, a: Byte end;
var
rgb: TRGBA;
rgb0: TRGBA absolute AColorValue;
dist: Double;
minDist: Double;
i: Integer;
n: Integer;
begin
Result := scNotDefined;
minDist := 1E108;
if AMaxPaletteCount = -1 then
n := Length(FPalette)
else
n := Min(Length(FPalette), AMaxPaletteCount);
for i:=0 to n-1 do
begin
rgb := TRGBA(GetPaletteColor(i));
dist := sqr(rgb.r - rgb0.r) + sqr(rgb.g - rgb0.g) + sqr(rgb.b - rgb0.b);
if dist < minDist then
begin
Result := i;
minDist := dist;
end;
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Converts a fpspreadsheet color into into a string RRGGBB. Converts a fpspreadsheet color into into a string RRGGBB.
Note that colors are written to xls files as ABGR (where A is 0). Note that colors are written to xls files as ABGR (where A is 0).
@ -7757,26 +7697,6 @@ begin
AName := Format('%.2x%.2x%.2x', [R, G, B]); AName := Format('%.2x%.2x%.2x', [R, G, B]);
end; end;
{@@ ----------------------------------------------------------------------------
Reads the rgb color for the given index from the current palette. Can be
type-cast to TColor for usage in GUI applications.
@param AColorIndex Index of the color considered
@return A number containing the rgb components in little-endian notation.
-------------------------------------------------------------------------------}
function TsWorkbook.GetPaletteColor(AColorIndex: TsColor): TsColorValue;
begin
if (AColorIndex >= 0) and (AColorIndex < GetPaletteSize) then
begin
if ((FPalette = nil) or (Length(FPalette) = 0)) then
Result := DEFAULT_PALETTE[AColorIndex]
else
Result := FPalette[AColorIndex];
end
else
Result := $000000; // "black" as default
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Converts the palette color of the given index to a string that can be used Converts the palette color of the given index to a string that can be used
in HTML code. For ODS. in HTML code. For ODS.
@ -7790,36 +7710,6 @@ begin
Result := ColorToHTMLColorStr(GetPaletteColor(AColorIndex)); Result := ColorToHTMLColorStr(GetPaletteColor(AColorIndex));
end; end;
{@@ ----------------------------------------------------------------------------
Replaces a color value of the current palette by a new value. The color must
be given as ABGR (little-endian), with A=0).
@param AColorIndex Palette index of the color to be replaced
@param AColorValue Number containing the rgb components of the new color
-------------------------------------------------------------------------------}
procedure TsWorkbook.SetPaletteColor(AColorIndex: TsColor;
AColorValue: TsColorValue);
begin
if (AColorIndex >= 0) and (AColorIndex < GetPaletteSize) then
begin
if ((FPalette = nil) or (Length(FPalette) = 0)) then
DEFAULT_PALETTE[AColorIndex] := AColorValue
else
FPalette[AColorIndex] := AColorValue;
end;
end;
{@@ ----------------------------------------------------------------------------
Returns the count of palette colors
-------------------------------------------------------------------------------}
function TsWorkbook.GetPaletteSize: Integer;
begin
if (FPalette = nil) or (Length(FPalette) = 0) then
Result := High(DEFAULT_PALETTE) + 1
else
Result := Length(FPalette);
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Instructs the workbook to take colors from the default palette. Is called Instructs the workbook to take colors from the default palette. Is called
from ODS reader because ODS does not have a palette. Without a palette the from ODS reader because ODS does not have a palette. Without a palette the
@ -7919,7 +7809,7 @@ begin
end; end;
Result := false; Result := false;
end; end;
*)
{******************************************************************************* {*******************************************************************************
* TsBasicSpreadReaderWriter * * TsBasicSpreadReaderWriter *
@ -7963,7 +7853,6 @@ end;
procedure TsBasicSpreadWriter.CheckLimitations; procedure TsBasicSpreadWriter.CheckLimitations;
var var
lastCol, lastRow: Cardinal; lastCol, lastRow: Cardinal;
i, n: Integer;
begin begin
Workbook.GetLastRowColIndex(lastRow, lastCol); Workbook.GetLastRowColIndex(lastRow, lastCol);
@ -7974,22 +7863,10 @@ begin
// Check column count // Check column count
if lastCol >= FLimitations.MaxColCount then if lastCol >= FLimitations.MaxColCount then
Workbook.AddErrorMsg(rsMaxColsExceeded, [lastCol+1, FLimitations.MaxColCount]); Workbook.AddErrorMsg(rsMaxColsExceeded, [lastCol+1, FLimitations.MaxColCount]);
// Check color count.
n := Workbook.GetPaletteSize;
if n > FLimitations.MaxPaletteSize then
for i:= FLimitations.MaxPaletteSize to n-1 do
if Workbook.UsesColor(i) then
begin
Workbook.AddErrorMsg(rsTooManyPaletteColors, [n, FLimitations.MaxPaletteSize]);
break;
end;
end; end;
initialization initialization
// Default palette
MakeLEPalette(@DEFAULT_PALETTE, Length(DEFAULT_PALETTE));
finalization finalization
SetLength(GsSpreadFormats, 0); SetLength(GsSpreadFormats, 0);

View File

@ -28,7 +28,7 @@ interface
uses uses
Classes, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList, Classes, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList,
LResources, LResources,
fpstypes, fpspreadsheet, {%H-}fpsAllFormats; fpstypes, fpspalette, fpspreadsheet, {%H-}fpsAllFormats;
type type
{@@ Event handler procedure for displaying a message if an error or {@@ Event handler procedure for displaying a message if an error or
@ -42,7 +42,7 @@ type
TsNotificationItem = (lniWorkbook, TsNotificationItem = (lniWorkbook,
lniWorksheet, lniWorksheetAdd, lniWorksheetRemoving, lniWorksheetRemove, lniWorksheet, lniWorksheetAdd, lniWorksheetRemoving, lniWorksheetRemove,
lniWorksheetRename, lniWorksheetRename,
lniCell, lniSelection, lniAbortSelection, lniRow, lniPalette); lniCell, lniSelection, lniAbortSelection, lniRow); //, lniPalette);
{@@ This set accompanies the notification between WorkbookSource and visual {@@ This set accompanies the notification between WorkbookSource and visual
controls and describes which items have changed in the spreadsheet. } controls and describes which items have changed in the spreadsheet. }
TsNotificationItems = set of TsNotificationItem; TsNotificationItems = set of TsNotificationItem;
@ -78,7 +78,7 @@ type
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = 0); AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = 0);
procedure SetFileName(const AFileName: TFileName); procedure SetFileName(const AFileName: TFileName);
procedure SetOptions(AValue: TsWorkbookOptions); procedure SetOptions(AValue: TsWorkbookOptions);
procedure WorkbookChangedPaletteHandler(Sender: TObject); // procedure WorkbookChangedPaletteHandler(Sender: TObject);
procedure WorkbookOpenedHandler(Sender: TObject); procedure WorkbookOpenedHandler(Sender: TObject);
procedure WorksheetAddedHandler(Sender: TObject; ASheet: TsWorksheet); procedure WorksheetAddedHandler(Sender: TObject; ASheet: TsWorksheet);
procedure WorksheetChangedHandler(Sender: TObject; ASheet: TsWorksheet); procedure WorksheetChangedHandler(Sender: TObject; ASheet: TsWorksheet);
@ -436,6 +436,9 @@ type
property FixedCols default 0; property FixedCols default 0;
end; end;
var
ComboColors: TsPalette = nil;
procedure Register; procedure Register;
@ -445,7 +448,6 @@ uses
Types, Math, TypInfo, LCLType, LCLProc, Dialogs, Forms, Types, Math, TypInfo, LCLType, LCLProc, Dialogs, Forms,
fpsStrings, fpsUtils; fpsStrings, fpsUtils;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Registers the spreadsheet components in the Lazarus component palette, Registers the spreadsheet components in the Lazarus component palette,
page "FPSpreadsheet". page "FPSpreadsheet".
@ -797,7 +799,7 @@ begin
FWorkbook.OnRemovingWorksheet := @WorksheetRemovingHandler; FWorkbook.OnRemovingWorksheet := @WorksheetRemovingHandler;
FWorkbook.OnRenameWorksheet := @WorksheetRenamedHandler; FWorkbook.OnRenameWorksheet := @WorksheetRenamedHandler;
FWorkbook.OnSelectWorksheet := @WorksheetSelectedHandler; FWorkbook.OnSelectWorksheet := @WorksheetSelectedHandler;
FWorkbook.OnChangePalette := @WorkbookChangedPaletteHandler; // FWorkbook.OnChangePalette := @WorkbookChangedPaletteHandler;
// Pass options to workbook // Pass options to workbook
SetOptions(FOptions); SetOptions(FOptions);
end; end;
@ -1240,7 +1242,7 @@ begin
EnableControls; EnableControls;
end; end;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Event handler called whenever the palette of the workbook is changed. Event handler called whenever the palette of the workbook is changed.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -1248,7 +1250,7 @@ procedure TsWorkbookSource.WorkbookChangedPaletteHandler(Sender: TObject);
begin begin
Unused(Sender); Unused(Sender);
NotifyListeners([lniPalette]); NotifyListeners([lniPalette]);
end; end; *)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Event handler called whenever a new workbook is opened. Event handler called whenever a new workbook is opened.
@ -1970,7 +1972,7 @@ begin
Brush.Style := bsClear; Brush.Style := bsClear;
end else end else
begin begin
Brush.Color := Workbook.GetPaletteColor(clr); Brush.Color := clr and $00FFFFFF;
Brush.Style := bsSolid; Brush.Style := bsSolid;
end; end;
Pen.Color := clBlack; Pen.Color := clBlack;
@ -2010,6 +2012,7 @@ procedure TsCellCombobox.ExtractFromCell(ACell: PCell);
var var
fnt: TsFont; fnt: TsFont;
clr: TsColor; clr: TsColor;
idx: Integer;
begin begin
case FFormatItem of case FFormatItem of
cfiFontName: cfiFontName:
@ -2091,7 +2094,7 @@ var
begin begin
Unused(AData); Unused(AData);
if (Worksheet = nil) or if (Worksheet = nil) or
([lniCell, lniSelection, lniPalette]*AChangedItems = []) ([lniCell, lniSelection]*AChangedItems = [])
then then
exit; exit;
@ -2100,11 +2103,9 @@ begin
(lniSelection in AChangedItems) (lniSelection in AChangedItems)
then then
ExtractFromCell(activeCell); ExtractFromCell(activeCell);
{
if (FFormatItem in [cfiFontColor, cfiBorderColor, cfiBackgroundColor]) and if (FFormatItem in [cfiFontColor, cfiBorderColor, cfiBackgroundColor]) then
(lniPalette in AChangedItems) Populate; }
then
Populate;
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -2136,6 +2137,7 @@ end;
procedure TsCellCombobox.Populate; procedure TsCellCombobox.Populate;
var var
i: Integer; i: Integer;
clr: TsColor;
begin begin
if Workbook = nil then if Workbook = nil then
exit; exit;
@ -2145,18 +2147,19 @@ begin
Items.Assign(Screen.Fonts); Items.Assign(Screen.Fonts);
cfiFontSize: cfiFontSize:
Items.CommaText := '8,9,10,11,12,13,14,16,18,20,22,24,26,28,32,36,48,72'; Items.CommaText := '8,9,10,11,12,13,14,16,18,20,22,24,26,28,32,36,48,72';
cfiFontColor: cfiBackgroundColor,
for i:=0 to Workbook.GetPaletteSize-1 do cfiFontColor,
Items.AddObject(Workbook.GetColorName(i), TObject(PtrInt(i)));
cfiBackgroundColor:
begin
Items.AddObject('(none)', TObject(scTransparent));
for i:=0 to Workbook.GetPaletteSize-1 do
Items.AddObject(Workbook.GetColorName(i), TObject(PtrInt(i)));
end;
cfiBorderColor: cfiBorderColor:
for i:=0 to Workbook.GetPaletteSize-1 do begin
Items.AddObject(Workbook.GetColorName(i), TObject(PtrInt(i))); Items.Clear;
if FFormatItem = cfiBackgroundColor then
Items.AddObject('(none)', TObject(scTransparent));
for i:=0 to ComboColors.Count-1 do
begin
clr := ComboColors[i];
Items.AddObject(GetColorName(clr), TObject(PtrInt(clr)));
end;
end;
else else
raise Exception.Create('[TsCellCombobox.Populate] Unknown cell format item.'); raise Exception.Create('[TsCellCombobox.Populate] Unknown cell format item.');
end; end;
@ -2673,8 +2676,8 @@ begin
else else
AStrings.Add(Format('BorderStyles[%s]=%s, %s', [ AStrings.Add(Format('BorderStyles[%s]=%s, %s', [
GetEnumName(TypeInfo(TsCellBorder), ord(cb)), GetEnumName(TypeInfo(TsCellBorder), ord(cb)),
GetEnumName(TypeInfo(TsLineStyle), ord(fmt.BorderStyles[cbEast].LineStyle)), GetEnumName(TypeInfo(TsLineStyle), ord(fmt.BorderStyles[cb].LineStyle)),
Workbook.GetColorName(fmt.BorderStyles[cbEast].Color)])); GetColorName(fmt.BorderStyles[cb].Color)]));
if (ACell = nil) or not (uffBackground in fmt.UsedformattingFields) then if (ACell = nil) or not (uffBackground in fmt.UsedformattingFields) then
begin begin
@ -2685,10 +2688,10 @@ begin
begin begin
AStrings.Add(Format('Style=%s', [ AStrings.Add(Format('Style=%s', [
GetEnumName(TypeInfo(TsFillStyle), ord(fmt.Background.Style))])); GetEnumName(TypeInfo(TsFillStyle), ord(fmt.Background.Style))]));
AStrings.Add(Format('PatternColor=%d (%s)', [ AStrings.Add(Format('PatternColor=$%.8x (%s)', [
fmt.Background.FgColor, Workbook.GetColorName(fmt.Background.FgColor)])); fmt.Background.FgColor, GetColorName(fmt.Background.FgColor)]));
AStrings.Add(Format('BackgroundColor=%d (%s)', [ AStrings.Add(Format('BackgroundColor=$%.8x (%s)', [
fmt.Background.BgColor, Workbook.GetColorName(fmt.Background.BgColor)])); fmt.Background.BgColor, GetColorName(fmt.Background.BgColor)]));
end; end;
if (ACell = nil) or not (uffNumberFormat in fmt.UsedFormattingFields) then if (ACell = nil) or not (uffNumberFormat in fmt.UsedFormattingFields) then
@ -2949,12 +2952,16 @@ initialization
CellClipboard := TsCellList.Create; CellClipboard := TsCellList.Create;
ComboColors := TsPalette.Create;
ComboColors.AddExcelColors;
RegisterPropertyToSkip(TsSpreadsheetInspector, 'RowHeights', 'For compatibility with older Laz versions.', ''); RegisterPropertyToSkip(TsSpreadsheetInspector, 'RowHeights', 'For compatibility with older Laz versions.', '');
RegisterPropertyToSkip(TsSpreadsheetInspector, 'ColWidths', 'For compatibility with older Laz versions.', ''); RegisterPropertyToSkip(TsSpreadsheetInspector, 'ColWidths', 'For compatibility with older Laz versions.', '');
finalization finalization
CellClipboard.Free; CellClipboard.Free;
if ComboColors <> nil then ComboColors.Free;
end. end.

View File

@ -247,7 +247,6 @@ type
{ Utilities related to Workbooks } { Utilities related to Workbooks }
procedure Convert_sFont_to_Font(sFont: TsFont; AFont: TFont); procedure Convert_sFont_to_Font(sFont: TsFont; AFont: TFont);
procedure Convert_Font_to_sFont(AFont: TFont; sFont: TsFont); procedure Convert_Font_to_sFont(AFont: TFont; sFont: TsFont);
function FindNearestPaletteIndex(AColor: TColor): TsColor;
{ Interfacing with WorkbookSource} { Interfacing with WorkbookSource}
procedure ListenerNotification(AChangedItems: TsNotificationItems; procedure ListenerNotification(AChangedItems: TsNotificationItems;
@ -1392,15 +1391,15 @@ begin
fsSolidFill: fsSolidFill:
begin begin
Canvas.Brush.Style := bsSolid; Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := Workbook.GetPaletteColor(fmt^.Background.FgColor); Canvas.Brush.Color := fmt^.Background.FgColor and $00FFFFFF;
end; end;
else else
if fmt^.Background.BgColor = scTransparent if fmt^.Background.BgColor = scTransparent
then bgcolor := Color then bgcolor := Color
else bgcolor := Workbook.GetPaletteColor(fmt^.Background.BgColor); else bgcolor := fmt^.Background.BgColor and $00FFFFFF;
if fmt^.Background.FgColor = scTransparent if fmt^.Background.FgColor = scTransparent
then fgcolor := Color then fgcolor := Color
else fgcolor := Workbook.GetPaletteColor(fmt^.Background.FgColor); else fgcolor := fmt^.Background.FgColor and $00FFFFFF;
CreateFillPattern(FillPatternBitmap, fmt^.Background.Style, fgColor, bgColor); CreateFillPattern(FillPatternBitmap, fmt^.Background.Style, fgColor, bgColor);
Canvas.Brush.Style := bsImage; Canvas.Brush.Style := bsImage;
Canvas.Brush.Bitmap := FillPatternBitmap; Canvas.Brush.Bitmap := FillPatternBitmap;
@ -1424,7 +1423,7 @@ begin
begin begin
Canvas.Font.Name := fnt.FontName; Canvas.Font.Name := fnt.FontName;
Canvas.Font.Size := round(fnt.Size); Canvas.Font.Size := round(fnt.Size);
Canvas.Font.Color := Workbook.GetPaletteColor(fnt.Color); Canvas.Font.Color := fnt.Color and $00FFFFFF;
style := []; style := [];
if fssBold in fnt.Style then Include(style, fsBold); if fssBold in fnt.Style then Include(style, fsBold);
if fssItalic in fnt.Style then Include(style, fsItalic); if fssItalic in fnt.Style then Include(style, fsItalic);
@ -1444,7 +1443,7 @@ begin
if (nfkHasColor in numFmt.Sections[sidx].Kind) then if (nfkHasColor in numFmt.Sections[sidx].Kind) then
begin begin
clr := numFmt.Sections[sidx].Color; clr := numFmt.Sections[sidx].Color;
Canvas.Font.Color := Workbook.GetPaletteColor(clr); Canvas.Font.Color := clr and $00FFFFFF;
end; end;
end; end;
// Wordwrap, text alignment and text rotation are handled by "DrawTextInCell". // Wordwrap, text alignment and text rotation are handled by "DrawTextInCell".
@ -1569,7 +1568,7 @@ const
begin begin
Canvas.Pen.Style := PEN_STYLES[ABorderStyle.LineStyle]; Canvas.Pen.Style := PEN_STYLES[ABorderStyle.LineStyle];
Canvas.Pen.Width := PEN_WIDTHS[ABorderStyle.LineStyle]; Canvas.Pen.Width := PEN_WIDTHS[ABorderStyle.LineStyle];
Canvas.Pen.Color := Workbook.GetPaletteColor(ABorderStyle.Color); Canvas.Pen.Color := ABorderStyle.Color and $00FFFFFF;
Canvas.Pen.EndCap := pecSquare; Canvas.Pen.EndCap := pecSquare;
width3 := (ABorderStyle.LineStyle in [lsThick, lsDouble]); width3 := (ABorderStyle.LineStyle in [lsThick, lsDouble]);
@ -2282,6 +2281,7 @@ begin
end; end;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
The "colors" used by the spreadsheet are indexes into the workbook's color The "colors" used by the spreadsheet are indexes into the workbook's color
palette. If the user wants to set a color to a particular RGB value this is palette. If the user wants to set a color to a particular RGB value this is
@ -2294,7 +2294,7 @@ function TsCustomWorksheetGrid.FindNearestPaletteIndex(AColor: TColor): TsColor;
begin begin
Result := fpsVisualUtils.FindNearestPaletteIndex(Workbook, AColor); Result := fpsVisualUtils.FindNearestPaletteIndex(Workbook, AColor);
end; end;
*)
(* (*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Notification by the workbook link that a cell has been modified. --> Repaint. Notification by the workbook link that a cell has been modified. --> Repaint.

View File

@ -95,7 +95,7 @@ type
procedure AddBuiltinNumFormats; virtual; procedure AddBuiltinNumFormats; virtual;
function FindNumFormatInList(ANumFormatStr: String): Integer; function FindNumFormatInList(ANumFormatStr: String): Integer;
function FixColor(AColor: TsColor): TsColor; virtual; // function FixColor(AColor: TsColor): TsColor; virtual;
procedure FixFormat(ACell: PCell); virtual; procedure FixFormat(ACell: PCell); virtual;
procedure GetSheetDimensions(AWorksheet: TsWorksheet; procedure GetSheetDimensions(AWorksheet: TsWorksheet;
out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual; out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual;
@ -404,7 +404,7 @@ begin
exit; exit;
Result := -1; Result := -1;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
If a color index is greater then the maximum palette color count this If a color index is greater then the maximum palette color count this
color is replaced by the closest palette color. color is replaced by the closest palette color.
@ -420,7 +420,7 @@ function TsCustomSpreadWriter.FixColor(AColor: TsColor): TsColor;
begin begin
Result := AColor; Result := AColor;
end; end;
*)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
If formatting features of a cell are not supported by the destination file If formatting features of a cell are not supported by the destination file
format of the writer, here is the place to apply replacements. format of the writer, here is the place to apply replacements.

View File

@ -67,6 +67,66 @@ resourcestring
rsCannotSortMerged = 'The cell range cannot be sorted because it contains merged cells.'; rsCannotSortMerged = 'The cell range cannot be sorted because it contains merged cells.';
// Colors
rsAqua = 'aqua';
rsBeige = 'beige';
rsBlack = 'black';
rsBlue = 'blue';
rsBlueGray = 'blue gray';
rsBrown = 'brown';
rsCoral = 'coral';
rsCyan = 'cyan';
rsDarkBlue = 'dark blue';
rsDarkGreen = 'dark green';
rsDarkPurple = 'dark purple';
rsDarkRed = 'dark red';
rsDarkTeal = 'dark teal';
rsGold = 'gold';
rsGray = 'gray';
rsGray10pct = '10% gray';
rsGray20pct = '20% gray';
rsGray25pct = '25% gray';
rsGray40pct = '40% gray';
rsGray50pct = '50% gray';
rsGray80pct = '80% gray';
rsGreen = 'green';
rsIceBlue = 'ice blue';
rsIndigo = 'indigo';
rsIvory = 'ivory';
rsLavander = 'lavander';
rsLightBlue = 'light blue';
rsLightGreen = 'light green';
rsLightOrange = 'light orange';
rsLightTurquoise = 'light turquoise';
rsLightYellow = 'light yellow';
rsLime = 'lime';
rsMagenta = 'magenta';
rsNavy = 'navy';
rsOceanBlue = 'ocean blue';
rsOlive = 'olive';
rsOliveGreen = 'olive green';
rsOrange = 'orange';
rsPaleBlue = 'pale blue';
rsPeriwinkle = 'periwinkle';
rsPink = 'pink';
rsPlum = 'plum';
rsPurple = 'purple';
rsRed = 'red';
rsRose = 'rose';
rsSeaGreen = 'sea green';
rsSilver = 'silver';
rsSkyBlue = 'sky blue';
rsTan = 'tan';
rsTeal = 'teal';
rsVeryDarkGreen = 'very dark green';
rsViolet = 'violet';
rsWheat = 'wheat';
rsWhite = 'white';
rsYellow = 'yellow';
rsNotDefined = 'not defined';
rsTransparent = 'transparent';
rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings? rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings?
rsFALSE = 'FALSE'; rsFALSE = 'FALSE';
rsErrEmptyIntersection = '#NULL!'; rsErrEmptyIntersection = '#NULL!';

View File

@ -264,92 +264,147 @@ type
{@@ Indicates vertical text alignment in cells } {@@ Indicates vertical text alignment in cells }
TsVertAlignment = (vaDefault, vaTop, vaCenter, vaBottom); TsVertAlignment = (vaDefault, vaTop, vaCenter, vaBottom);
{@@ {@@ Colors in fpspreadsheet are given as rgb values in little-endian notation
Colors in fpspreadsheet are given as indices into a palette. (i.e. "r" is the low-value byte). The highest-value byte, if not zero,
Use the workbook's GetPaletteColor to determine the color rgb value as indicates special colors. }
little-endian (with "r" being the low-value byte, in agreement with TColor). TsColor = DWord;
The data type for rgb values is TsColorValue. }
TsColor = Word;
{@@
These are some constants for color indices into the default palette.
Note, however, that if a different palette is used there may be more colors,
and the names of the color constants may no longer be correct.
}
const const
{@@ Index of <b>black</b> color in the standard color palettes } {@@ These are some important rgb color volues.
scBlack = $00; }
{@@ Index of <b>white</b> color in the standard color palettes } {@@ rgb value of <b>black</b> color, BIFF2 palette index 0, BIFF8 index 8}
scWhite = $01; scBlack = $00000000;
{@@ Index of <b>red</b> color in the standard color palettes } {@@ rgb value of <b>white</b> color, BIFF2 palette index 1, BIFF8 index 9 }
scRed = $02; scWhite = $00FFFFFF;
{@@ Index of <b>green</b> color in the standard color palettes } {@@ rgb value of <b>red</b> color, BIFF2 palette index 2, BIFF8 index 10 }
scGreen = $03; scRed = $000000FF;
{@@ Index of <b>blue</b> color in the standard color palettes } {@@ rgb value of <b>green</b> color, BIFF2 palette index 3, BIFF8 index 11 }
scBlue = $04; scGreen = $0000FF00;
{@@ Index of <b>yellow</b> color in the standard color palettes } {@@ rgb value of <b>blue</b> color, BIFF2 palette index 4, BIFF8 indexes 12 and 39}
scYellow = $05; scBlue = $00FF0000;
{@@ Index of <b>magenta</b> color in the standard color palettes } {@@ rgb value of <b>yellow</b> color, BIFF2 palette index 5, BIFF8 indexes 13 and 34}
scMagenta = $06; scYellow = $0000FFFF;
{@@ Index of <b>cyan</b> color in the standard color palettes } {@@ rgb value of <b>magenta</b> color, BIFF2 palette index 6, BIFF8 index 14 and 33}
scCyan = $07; scMagenta = $00FF00FF;
{@@ Index of <b>dark red</b> color in the standard color palettes } scPink = $00FE00FE;
scDarkRed = $08; {@@ rgb value of <b>cyan</b> color, BIFF2 palette index 7, BIFF8 indexes 15}
{@@ Index of <b>dark green</b> color in the standard color palettes } scCyan = $00FFFF00;
scDarkGreen = $09; scTurquoise = scCyan;
{@@ Index of <b>dark blue</b> color in the standard color palettes } {@@ rgb value of <b>dark red</b> color, BIFF8 indexes 16 and 35}
scDarkBlue = $0A; scDarkRed = $00000080;
{@@ Index of <b>"navy"</b> color (dark blue) in the standard color palettes } {@@ rgb value of <b>dark green</b> color, BIFF8 index 17 }
scNavy = $0A; scDarkGreen = $00008000;
{@@ Index of <b>olive</b> color in the standard color palettes } {@@ rgb value of <b>dark blue</b> color }
scOlive = $0B; scDarkBlue = $008B0000;
{@@ Index of <b>purple</b> color in the standard color palettes } {@@ rgb value of <b>"navy"</b> color, BIFF8 palette indexes 18 and 32 }
scPurple = $0C; scNavy = $00800000;
{@@ Index of <b>teal</b> color in the standard color palettes } {@@ rgb value of <b>olive</b> color }
scTeal = $0D; scOlive = $00008080;
{@@ Index of <b>silver</b> color in the standard color palettes } {@@ rgb value of <b>purple</b> color, BIFF8 palette indexes 20 and 36 }
scSilver = $0E; scPurple = $00800080;
{@@ Index of <b>grey</b> color in the standard color palettes } {@@ rgb value of <b>teal</b> color, BIFF8 palette index 21 and 38 }
scGrey = $0F; scTeal = $00808000;
{@@ Index of <b>gray</b> color in the standard color palettes } {@@ rgb value of <b>silver</b> color }
scGray = $0F; // redefine to allow different spelling scSilver = $00C0C0C0;
{@@ Index of a <b>10% grey</b> color in the standard color palettes } scGray25pct = scSilver;
scGrey10pct = $10; {@@ rgb value of <b>grey</b> color }
{@@ Index of a <b>10% gray</b> color in the standard color palettes } scGray = $00808080;
scGray10pct = $10; {@@ rgb value of <b>gray</b> color }
{@@ Index of a <b>20% grey</b> color in the standard color palettes } scGrey = scGray; // redefine to allow different spelling
scGrey20pct = $11; scGray50pct = scGray;
{@@ Index of a <b>20% gray</b> color in the standard color palettes } {@@ rgb value of a <b>10% grey</b> color }
scGray20pct = $11; scGray10pct = $00E6E6E6;
{@@ Index of <b>orange</b> color in the standard color palettes } {@@ rgb value of a <b>10% gray</b> color }
scOrange = $12; scGrey10pct = scGray10pct;
{@@ Index of <b>dark brown</b> color in the standard color palettes } {@@ rgb value of a <b>20% grey</b> color }
scDarkbrown = $13; scGray20pct = $00CCCCCC;
{@@ Index of <b>brown</b> color in the standard color palettes } {@@ rgb value of a <b>20% gray</b> color }
scBrown = $14; scGrey20pct = scGray20pct;
{@@ Index of <b>beige</b> color in the standard color palettes } {@@ rgb value of <b>periwinkle</b> color, BIFF8 palette index 24 }
scBeige = $15; scPeriwinkle = $00FF9999;
{@@ Index of <b>"wheat"</b> color (yellow-orange) in the standard color palettes } {@@ rgb value of <b>plum</b> color, BIFF8 palette indexes 25 and 61 }
scWheat = $16; scPlum = $00663399;
{@@ rgb value of <b>ivory</b> color, BIFF8 palette index 26 }
scIvory = $00CCFFFF;
{@@ rgb value of <b>light turquoise</b> color, BIFF8 palette indexes 27 and 41 }
scLightTurquoise = $00FFFFCC;
{@@ rgb value of <b>dark purple</b> color, BIFF8 palette index 28 }
scDarkPurple = $00660066;
{@@ rgb value of <b>coral</b> color, BIFF8 palette index 29 }
scCoral = $008080FF;
{@@ rgb value of <b>ocean blue</b> color, BIFF8 palette index 30 }
scOceanBlue = $00CC6600;
{@@ rgb value of <b>ice blue</b> color, BIFF8 palette index 31 }
scIceBlue = $00FFCCCC;
{@@ rgb value of <b>sky blue </b>color, BIFF8 palette index 40 }
scSkyBlue = $00FFCC00;
{@@ rgb value of <b>light green</b> color, BIFF8 palette index 42 }
scLightGreen = $00CCFFCC;
{@@ rgb value of <b>light yellow</b> color, BIFF8 palette index 43 }
scLightYellow = $0099FFFF;
{@@ rgb value of <b>pale blue</b> color, BIFF8 palette index 44 }
scPaleBlue = $00FFCC99;
{@@ rgb value of <b>rose</b> color, BIFF8 palette index 45 }
scRose = $00CC99FF;
{@@ rgb value of <b>lavander</b> color, BIFF8 palette index 46 }
scLavander = $00FF99CC;
{@@ rgb value of <b>tan</b> color, BIFF8 palette index 47 }
scTan = $0099CCFF;
{@@ rgb value of <b>light blue</b> color, BIFF8 palette index 48 }
scLightBlue = $00FF6633;
{@@ rgb value of <b>aqua</b> color, BIFF8 palette index 49 }
scAqua = $00CCCC33;
{@@ rgb value of <b>lime</b> color, BIFF8 palette index 50 }
scLime = $0000CC99;
{@@ rgb value of <b>golden</b> color, BIFF8 palette index 51 }
scGold = $0000CCFF;
{@@ rgb value of <b>light orange</b> color, BIFF8 palette index 52 }
scLightOrange = $000099FF;
{@@ rgb value of <b>orange</b> color, BIFF8 palette index 53 }
scOrange = $000066FF;
{@@ rgb value of <b>blue gray</b>, BIFF8 palette index 54 }
scBlueGray = $00996666;
scBlueGrey = scBlueGray;
{@@ rgb value of <b>gray 40%</b>, BIFF8 palette index 55 }
scGray40pct = $00969696;
{@@ rgb value of <b>dark teal</b>, BIFF8 palette index 56 }
scDarkTeal = $00663300;
{@@ rgb value of <b>sea green</b>, BIFF8 palette index 57 }
scSeaGreen = $00669933;
{@@ rgb value of <b>very dark green</b>, BIFF8 palette index 58 }
scVeryDarkGreen = $00003300;
{@@ rgb value of <b>olive green</b> color, BIFF8 palette index 59 }
scOliveGreen = $00003333;
{@@ rgb value of <b>brown</b> color, BIFF8 palette index 60 }
scBrown = $00003399;
{@@ rgb value of <b>indigo</b> color, BIFF8 palette index 62 }
scIndigo = $00993333;
{@@ rgb value of <b>80% gray</b>, BIFF8 palette index 63 }
scGray80pct = $00333333;
scGrey80pct = scGray80pct;
// not sure - but I think the mechanism with scRGBColor is not working... // {@@ rgb value of <b>orange</b> color }
// Will be removed sooner or later... // scOrange = $0000A5FF;
scRGBColor = $FFFD; {@@ rgb value of <b>dark brown</b> color }
scDarkBrown = $002D52A0;
// {@@ rgb value of <b>brown</b> color }
// scBrown = $003F85CD;
{@@ rgb value of <b>beige</b> color }
scBeige = $00DCF5F5;
{@@ rgb value of <b>"wheat"</b> color (yellow-orange) }
scWheat = $00B3DEF5;
{@@ Identifier for transparent color }
scTransparent = $FFFE;
{@@ Identifier for not-defined color } {@@ Identifier for not-defined color }
scNotDefined = $FFFF; scNotDefined = $40000000;
{@@ Identifier for transparent color }
scTransparent = $20000000;
{@@ Identifier for palette index encoded into the TsColor }
scPaletteIndexMask = $80000000;
{@@ Mask for the rgb components contained in the TsColor }
scRGBMask = $00FFFFFF;
type type
{@@ Data type for rgb color values }
TsColorValue = DWord;
{@@ Palette of color values. A "color value" is a DWord value containing
rgb colors. }
TsPalette = array[0..0] of TsColorValue;
PsPalette = ^TsPalette;
{@@ Font style (redefined to avoid usage of "Graphics" } {@@ Font style (redefined to avoid usage of "Graphics" }
TsFontStyle = (fssBold, fssItalic, fssStrikeOut, fssUnderline); TsFontStyle = (fssBold, fssItalic, fssStrikeOut, fssUnderline);
@ -365,7 +420,7 @@ type
Size: Single; // in "points" Size: Single; // in "points"
{@@ Font style, such as bold, italics etc. - see TsFontStyle} {@@ Font style, such as bold, italics etc. - see TsFontStyle}
Style: TsFontStyles; Style: TsFontStyles;
{@@ Text color given by the index into the workbook's color palette } {@@ Text color given as rgb value }
Color: TsColor; Color: TsColor;
end; end;

View File

@ -28,6 +28,9 @@ type
{@@ Set of characters } {@@ Set of characters }
TsDecsChars = set of char; TsDecsChars = set of char;
{@@ Color value, composed of r(ed), g(reen) and b(lue) components }
TRGBA = record r, g, b, a: byte end;
const const
{@@ Date formatting string for unambiguous date/time display as strings {@@ Date formatting string for unambiguous date/time display as strings
Can be used for text output when date/time cell support is not available } Can be used for text output when date/time cell support is not available }
@ -51,8 +54,6 @@ function WordLEtoN(AValue: Word): Word;
function DWordLEtoN(AValue: Cardinal): Cardinal; function DWordLEtoN(AValue: Cardinal): Cardinal;
function WideStringLEToN(const AValue: WideString): WideString; function WideStringLEToN(const AValue: WideString): WideString;
function LongRGBToExcelPhysical(const RGB: DWord): DWord;
// Other routines // Other routines
function ParseIntervalString(const AStr: string; function ParseIntervalString(const AStr: string;
out AFirstCellRow, AFirstCellCol, ACount: Cardinal; out AFirstCellRow, AFirstCellCol, ACount: Cardinal;
@ -140,13 +141,18 @@ function pxToPts(AValue, AScreenPixelsPerInch: Integer): Double; inline;
function PtsToPx(AValue: Double; AScreenPixelsPerInch: Integer): Integer; inline; function PtsToPx(AValue: Double; AScreenPixelsPerInch: Integer): Integer; inline;
function HTMLLengthStrToPts(AValue: String; DefaultUnits: String = 'pt'): Double; function HTMLLengthStrToPts(AValue: String; DefaultUnits: String = 'pt'): Double;
function HTMLColorStrToColor(AValue: String): TsColorValue;
function ColorToHTMLColorStr(AValue: TsColorValue; AExcelDialect: Boolean = false): String;
function UTF8TextToXMLText(AText: ansistring): ansistring; function UTF8TextToXMLText(AText: ansistring): ansistring;
function ValidXMLText(var AText: ansistring; ReplaceSpecialChars: Boolean = true): Boolean; function ValidXMLText(var AText: ansistring; ReplaceSpecialChars: Boolean = true): Boolean;
function TintedColor(AColor: TsColorValue; tint: Double): TsColorValue; function ColorToHTMLColorStr(AValue: TsColor; AExcelDialect: Boolean = false): String;
function HighContrastColor(AColorValue: TsColorValue): TsColor; function HTMLColorStrToColor(AValue: String): TsColor;
function GetColorName(AColor: TsColor): String;
function HighContrastColor(AColor: TsColor): TsColor;
function IsPaletteIndex(AColor: TsColor): Boolean;
function LongRGBToExcelPhysical(const RGB: DWord): DWord;
function SetAsPaletteIndex(AIndex: Integer): TsColor;
function TintedColor(AColor: TsColor; tint: Double): TsColor;
function AnalyzeCompareStr(AString: String; out ACompareOp: TsCompareOperation): String; function AnalyzeCompareStr(AString: String; out ACompareOp: TsCompareOperation): String;
@ -183,9 +189,6 @@ implementation
uses uses
Math, lazutf8, fpsStrings; Math, lazutf8, fpsStrings;
type
TRGBA = record r, g, b, a: byte end;
const const
POS_CURR_FMT: array[0..3] of string = ( POS_CURR_FMT: array[0..3] of string = (
// Format parameter 0 is "value", parameter 1 is "currency symbol" // Format parameter 0 is "value", parameter 1 is "currency symbol"
@ -356,29 +359,6 @@ begin
{$ENDIF} {$ENDIF}
end; end;
{@@ ----------------------------------------------------------------------------
Converts the RGB part of a LongRGB logical structure to its physical representation.
In other words: RGBA (where A is 0 and omitted in the function call) => ABGR
Needed for conversion of palette colors.
@param RGB DWord value containing RGBA bytes in big endian byte-order
@return DWord containing RGB bytes in little-endian byte-order (A = 0)
-------------------------------------------------------------------------------}
function LongRGBToExcelPhysical(const RGB: DWord): DWord;
begin
{$IFDEF FPC}
{$IFDEF ENDIAN_LITTLE}
result := RGB shl 8; //tags $00 at end for the A byte
result := SwapEndian(result); //flip byte order
{$ELSE}
//Big endian
result := RGB; //leave value as is //todo: verify if this turns out ok
{$ENDIF}
{$ELSE}
// messed up result
{$ENDIF}
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Parses strings like A5:A10 into an selection interval information Parses strings like A5:A10 into an selection interval information
@ -1968,13 +1948,83 @@ begin
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Converts a HTML color string to a TsColorValue. Need for the ODS file format. Determines the name of a color from its rgb value
-------------------------------------------------------------------------------}
function GetColorName(AColor: TsColor): string;
var
rgba: TRGBA absolute AColor;
begin
case AColor of
scAqua : Result := rsAqua;
scBeige : Result := rsBeige;
scBlack : Result := rsBlack;
scBlue : Result := rsBlue;
scBlueGray : Result := rsBlueGray;
scBrown : Result := rsBrown;
scCoral : Result := rsCoral;
scCyan : Result := rsCyan;
scDarkBlue : Result := rsDarkBlue;
scDarkGreen : Result := rsDarkGreen;
scDarkPurple : Result := rsDarkPurple;
scDarkRed : Result := rsDarkRed;
scDarkTeal : Result := rsDarkTeal;
scGold : Result := rsGold;
scGray : Result := rsGray;
scGray10pct : Result := rsGray10pct;
scGray20pct : Result := rsGray20pct;
scGray40pct : Result := rsGray40pct;
scGray80pct : Result := rsGray80pct;
scGreen : Result := rsGreen;
scIceBlue : Result := rsIceBlue;
scIndigo : Result := rsIndigo;
scIvory : Result := rsIvory;
scLavander : Result := rsLavander;
scLightBlue : Result := rsLightBlue;
scLightGreen : Result := rsLightGreen;
scLightOrange: Result := rsLightOrange;
scLightTurquoise: Result := rsLightTurquoise;
scLightYellow: Result := rsLightYellow;
scLime : Result := rsLime;
scMagenta : Result := rsMagenta;
scNavy : Result := rsNavy;
scOceanBlue : Result := rsOceanBlue;
scOlive : Result := rsOlive;
scOliveGreen : Result := rsOliveGreen;
scOrange : Result := rsOrange;
scPaleBlue : Result := rsPaleBlue;
scPeriwinkle : Result := rsPeriwinkle;
scPink : Result := rsPink;
scPlum : Result := rsPlum;
scPurple : Result := rsPurple;
scRed : Result := rsRed;
scRose : Result := rsRose;
scSeaGreen : Result := rsSeaGreen;
scSilver : Result := rsSilver;
scSkyBlue : Result := rsSkyBlue;
scTan : Result := rsTan;
scTeal : Result := rsTeal;
scVeryDarkGreen: Result := rsVeryDarkGreen;
// scViolet : Result := rsViolet;
scWheat : Result := rsWheat;
scWhite : Result := rsWhite;
scYellow : Result := rsYellow;
scTransparent: Result := rsTransparent;
scNotDefined : Result := rsNotDefined;
else if rgba.a = 0 then
Result := Format('r%d g%d b%d', [rgba.r, rgba.g, rgba.b])
else
Result := '';
end;
end;
{@@ ----------------------------------------------------------------------------
Converts a HTML color string to a TsColor alue. Needed for the ODS file format.
@param AValue HTML color string, such as '#FF0000' @param AValue HTML color string, such as '#FF0000'
@return rgb color value in little endian byte-sequence. This value is @return rgb color value in little endian byte-sequence. This value is
compatible with the TColor data type of the graphics unit. compatible with the TColor data type of the graphics unit.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function HTMLColorStrToColor(AValue: String): TsColorValue; function HTMLColorStrToColor(AValue: String): TsColor;
begin begin
if AValue = '' then if AValue = '' then
Result := scNotDefined Result := scNotDefined
@ -2022,13 +2072,11 @@ end;
i.e. in AARRGGBB notation, like '00FF0000' for "red" i.e. in AARRGGBB notation, like '00FF0000' for "red"
@return HTML-compatible string, like '#FF0000' (AExcelDialect = false) @return HTML-compatible string, like '#FF0000' (AExcelDialect = false)
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function ColorToHTMLColorStr(AValue: TsColorValue; AExcelDialect: Boolean = false): String; function ColorToHTMLColorStr(AValue: TsColor;
type AExcelDialect: Boolean = false): String;
TRGB = record r,g,b,a: Byte end;
var var
rgb: TRGB; rgb: TRGBA absolute AValue;
begin begin
rgb := TRGB(AValue);
if AExcelDialect then if AExcelDialect then
Result := Format('00%.2x%.2x%.2x', [rgb.r, rgb.g, rgb.b]) Result := Format('00%.2x%.2x%.2x', [rgb.r, rgb.g, rgb.b])
else else
@ -3069,6 +3117,23 @@ begin
end; end;
end; end;
{@@ ----------------------------------------------------------------------------
Constructs a TsColor from a palette index. It has bit 15 in the high-order
byte set.
-------------------------------------------------------------------------------}
function SetAsPaletteIndex(AIndex: Integer): TsColor;
begin
Result := (DWord(AIndex) and scRGBMask) or scPaletteIndexMask;
end;
{@@ ----------------------------------------------------------------------------
Checks whether the specified TsColor represents a palette index
-------------------------------------------------------------------------------}
function IsPaletteIndex(AColor: TsColor): Boolean;
begin
Result := AColor and scPaletteIndexMask = scPaletteIndexMask;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Excel defines theme colors and applies a "tint" factor (-1...+1) to darken Excel defines theme colors and applies a "tint" factor (-1...+1) to darken
or brighten them. or brighten them.
@ -3082,7 +3147,7 @@ end;
@param tint Factor (-1...+1) to be used for the operation @param tint Factor (-1...+1) to be used for the operation
@return Modified color @return Modified color
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TintedColor(AColor: TsColorValue; tint: Double): TsColorValue; function TintedColor(AColor: TsColor; tint: Double): TsColor;
const const
HLSMAX = 255; HLSMAX = 255;
var var
@ -3090,7 +3155,7 @@ var
h, l, s: Byte; h, l, s: Byte;
lum: Double; lum: Double;
begin begin
if tint = 0 then begin if (tint = 0) or (TRGBA(AColor).a <> 0) then begin
Result := AColor; Result := AColor;
exit; exit;
end; end;
@ -3119,18 +3184,42 @@ end;
Returns the color index for black or white depending on a color being "bright" Returns the color index for black or white depending on a color being "bright"
or "dark". or "dark".
@param AColorValue rgb color to be analyzed @param AColor rgb color to be analyzed
@return The color index for black (scBlack) if AColorValue is a "bright" color, @return The color index for black (scBlack) if AColorValue is a "bright" color,
or white (scWhite) if AColorValue is a "dark" color. or white (scWhite) if AColorValue is a "dark" color.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function HighContrastColor(AColorValue: TsColorvalue): TsColor; function HighContrastColor(AColor: TsColor): TsColor;
begin begin
if TRGBA(AColorValue).r + TRGBA(AColorValue).g + TRGBA(AColorValue).b < 3*128 then if TRGBA(AColor).r + TRGBA(AColor).g + TRGBA(AColor).b < 3*128 then
Result := scWhite Result := scWhite
else else
Result := scBlack; Result := scBlack;
end; end;
{@@ ----------------------------------------------------------------------------
Converts the RGB part of a LongRGB logical structure to its physical representation.
In other words: RGBA (where A is 0 and omitted in the function call) => ABGR
Needed for conversion of palette colors.
@param RGB DWord value containing RGBA bytes in big endian byte-order
@return DWord containing RGB bytes in little-endian byte-order (A = 0)
-------------------------------------------------------------------------------}
function LongRGBToExcelPhysical(const RGB: DWord): DWord;
begin
{$IFDEF FPC}
{$IFDEF ENDIAN_LITTLE}
result := RGB shl 8; //tags $00 at end for the A byte
result := SwapEndian(result); //flip byte order
{$ELSE}
//Big endian
result := RGB; //leave value as is //todo: verify if this turns out ok
{$ENDIF}
{$ELSE}
// messed up result
{$ENDIF}
end;
{$PUSH}{$HINTS OFF} {$PUSH}{$HINTS OFF}
{@@ Silence warnings due to an unused parameter } {@@ Silence warnings due to an unused parameter }
procedure Unused(const A1); procedure Unused(const A1);

View File

@ -10,7 +10,7 @@ uses
procedure Convert_sFont_to_Font(AWorkbook: TsWorkbook; sFont: TsFont; AFont: TFont); procedure Convert_sFont_to_Font(AWorkbook: TsWorkbook; sFont: TsFont; AFont: TFont);
procedure Convert_Font_to_sFont(AWorkbook: TsWorkbook; AFont: TFont; sFont: TsFont); procedure Convert_Font_to_sFont(AWorkbook: TsWorkbook; AFont: TFont; sFont: TsFont);
function FindNearestPaletteIndex(AWorkbook: TsWorkbook; AColor: TColor): TsColor; //function FindNearestPaletteIndex(AWorkbook: TsWorkbook; AColor: TColor): TsColor;
function WrapText(ACanvas: TCanvas; const AText: string; AMaxWidth: integer): string; function WrapText(ACanvas: TCanvas; const AText: string; AMaxWidth: integer): string;
@ -36,7 +36,7 @@ begin
if fssItalic in sFont.Style then AFont.Style := AFont.Style + [fsItalic]; if fssItalic in sFont.Style then AFont.Style := AFont.Style + [fsItalic];
if fssUnderline in sFont.Style then AFont.Style := AFont.Style + [fsUnderline]; if fssUnderline in sFont.Style then AFont.Style := AFont.Style + [fsUnderline];
if fssStrikeout in sFont.Style then AFont.Style := AFont.Style + [fsStrikeout]; if fssStrikeout in sFont.Style then AFont.Style := AFont.Style + [fsStrikeout];
AFont.Color := AWorkbook.GetPaletteColor(sFont.Color); AFont.Color := TColor(sFont.Color and $00FFFFFF);
end; end;
end; end;
@ -56,10 +56,10 @@ begin
if fsItalic in AFont.Style then Include(sFont.Style, fssItalic); if fsItalic in AFont.Style then Include(sFont.Style, fssItalic);
if fsUnderline in AFont.Style then Include(sFont.Style, fssUnderline); if fsUnderline in AFont.Style then Include(sFont.Style, fssUnderline);
if fsStrikeout in AFont.Style then Include(sFont.Style, fssStrikeout); if fsStrikeout in AFont.Style then Include(sFont.Style, fssStrikeout);
sFont.Color := FindNearestPaletteIndex(AWorkbook, AFont.Color); sFont.Color := ColorToRGB(AFont.Color);
end; end;
end; end;
(*
function FindNearestPaletteIndex(AWorkbook: TsWorkbook; AColor: TColor): TsColor; function FindNearestPaletteIndex(AWorkbook: TsWorkbook; AColor: TColor): TsColor;
procedure ColorToHSL(RGB: TColor; out H, S, L : double); procedure ColorToHSL(RGB: TColor; out H, S, L : double);
@ -148,7 +148,7 @@ begin
end; end;
end; end;
end; end;
*)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Wraps text by inserting line ending characters so that the lines are not Wraps text by inserting line ending characters so that the lines are not
longer than AMaxWidth. longer than AMaxWidth.

View File

@ -28,7 +28,7 @@
This package is all you need if you don't want graphical components (like grids and charts)."/> This package is all you need if you don't want graphical components (like grids and charts)."/>
<License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/> <License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/>
<Version Major="1" Minor="5"/> <Version Major="1" Minor="5"/>
<Files Count="34"> <Files Count="35">
<Item1> <Item1>
<Filename Value="fpolestorage.pas"/> <Filename Value="fpolestorage.pas"/>
<UnitName Value="fpolestorage"/> <UnitName Value="fpolestorage"/>
@ -165,6 +165,10 @@ This package is all you need if you don't want graphical components (like grids
<Filename Value="fpsheaderfooterparser.pas"/> <Filename Value="fpsheaderfooterparser.pas"/>
<UnitName Value="fpsHeaderFooterParser"/> <UnitName Value="fpsHeaderFooterParser"/>
</Item34> </Item34>
<Item35>
<Filename Value="fpspalette.pas"/>
<UnitName Value="fpsPalette"/>
</Item35>
</Files> </Files>
<RequiredPkgs Count="2"> <RequiredPkgs Count="2">
<Item1> <Item1>

View File

@ -13,7 +13,7 @@ uses
uvirtuallayer_ole_helpers, uvirtuallayer_ole_types, uvirtuallayer_stream, uvirtuallayer_ole_helpers, uvirtuallayer_ole_types, uvirtuallayer_stream,
fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings, fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings,
fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher, fpsReaderWriter, fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher, fpsReaderWriter,
fpsNumFormat, fpsclasses, fpsHeaderFooterParser; fpsNumFormat, fpsclasses, fpsHeaderFooterParser, fpsPalette;
implementation implementation

View File

@ -80,8 +80,12 @@ type
procedure TestWriteRead_OOXML_Font_RandomPal; // palette 64, top 56 entries random procedure TestWriteRead_OOXML_Font_RandomPal; // palette 64, top 56 entries random
end; end;
implementation implementation
uses
fpsPalette;
const const
ColorsSheet = 'Colors'; ColorsSheet = 'Colors';
@ -111,47 +115,41 @@ var
row, col: Integer; row, col: Integer;
MyCell: PCell; MyCell: PCell;
TempFile: string; //write xls/xml to this file and read back from it TempFile: string; //write xls/xml to this file and read back from it
color: TsColor;
expectedRGB: DWord; expectedRGB: DWord;
currentRGB: DWord; currentRGB: DWord;
pal: Array of TsColorValue; palette: TsPalette;
i: Integer; i: Integer;
begin begin
TempFile:=GetTempFileName; TempFile:=GetTempFileName;
// Define palette
palette := TsPalette.Create;
try
case whichPalette of
5: palette.UseColors(PALETTE_BIFF5);
8: palette.UseColors(PALETTE_BIFF8);
999: begin // random palette: testing of color replacement
palette.UseColors(PALETTE_BIFF8);
for i:=8 to 63 do // first 8 colors must not be changed in Excel
palette[i] := random(256) + random(256) shr 8 + random(256) shr 16;
end;
else palette.AddBuiltinColors;
end;
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorkSheet:= MyWorkBook.AddWorksheet(ColorsSheet); MyWorkSheet:= MyWorkBook.AddWorksheet(ColorsSheet);
// Define palette
case whichPalette of
5: MyWorkbook.UsePalette(@PALETTE_BIFF5, Length(PALETTE_BIFF5));
8: MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
999: begin // Random palette: testing of color replacement
MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
for i:=8 to 63 do // first 8 colors cannot be changed
MyWorkbook.SetPaletteColor(i, random(256) + random(256) shr 8 + random(256) shr 16);
end;
// else use default palette
end;
// Remember all colors because ODS does not have a palette in the file; therefore
// we do not know which colors to expect.
SetLength(pal, MyWorkbook.GetPaletteSize);
for i:=0 to High(pal) do
pal[i] := MyWorkbook.GetPaletteColor(i);
// Write out all colors // Write out all colors
row := 0; row := 0;
col := 0; col := 0;
for color := 0 to MyWorkbook.GetPaletteSize-1 do begin for i := 0 to palette.Count-1 do begin
MyWorksheet.WriteUTF8Text(row, col, CELLTEXT); MyWorksheet.WriteUTF8Text(row, col, CELLTEXT);
MyWorksheet.WriteBackgroundColor(row, col, color); MyCell := MyWorksheet.WriteBackgroundColor(row, col, palette[i]);
MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then if MyCell = nil then
fail('Error in test code. Failed to get cell.'); fail('Error in test code. Failed to get cell.');
currentRGB := MyWorkbook.GetPaletteColor(MyWorksheet.ReadBackgroundColor(MyCell)); currentRGB := MyWorksheet.ReadBackgroundColor(MyCell);
expectedRGB := MyWorkbook.GetPaletteColor(color); expectedRGB := palette[i];
CheckEquals(expectedRGB, currentRGB, CheckEquals(expectedRGB, currentRGB,
'Test unsaved background color, cell ' + CellNotation(MyWorksheet,0,0)); 'Test unsaved background color, cell ' + CellNotation(MyWorksheet,0,0));
inc(row); inc(row);
@ -175,9 +173,8 @@ begin
MyCell := MyWorksheet.FindCell(row, col); MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then if MyCell = nil then
fail('Error in test code. Failed to get cell.'); fail('Error in test code. Failed to get cell.');
color := TsColor(row); currentRGB := MyWorksheet.ReadBackgroundColor(MyCell);
currentRGB := MyWorkbook.GetPaletteColor(MyWorksheet.ReadBackgroundColor(MyCell)); expectedRGB := palette[row];
expectedRGB := pal[color];
CheckEquals(expectedRGB, currentRGB, CheckEquals(expectedRGB, currentRGB,
'Test saved background color, cell '+CellNotation(MyWorksheet,Row,Col)); 'Test saved background color, cell '+CellNotation(MyWorksheet,Row,Col));
end; end;
@ -185,6 +182,10 @@ begin
MyWorkbook.Free; MyWorkbook.Free;
DeleteFile(TempFile); DeleteFile(TempFile);
end; end;
finally
palette.Free
end;
end; end;
procedure TSpreadWriteReadColorTests.TestWriteReadFontColors(AFormat: TsSpreadsheetFormat; procedure TSpreadWriteReadColorTests.TestWriteReadFontColors(AFormat: TsSpreadsheetFormat;
@ -201,47 +202,41 @@ var
row, col: Integer; row, col: Integer;
MyCell: PCell; MyCell: PCell;
TempFile: string; //write xls/xml to this file and read back from it TempFile: string; //write xls/xml to this file and read back from it
color, colorInFile: TsColor;
expectedRGB, currentRGB: DWord; expectedRGB, currentRGB: DWord;
pal: Array of TsColorValue; palette: TsPalette;
i: Integer; i: Integer;
begin begin
TempFile:=GetTempFileName; TempFile:=GetTempFileName;
// Define palette
palette := TsPalette.Create;
try
case whichPalette of
5: palette.UseColors(PALETTE_BIFF5);
8: palette.UseColors(PALETTE_BIFF8);
999: begin // random palette: testing of color replacement
palette.UseColors(PALETTE_BIFF8);
for i:=8 to 63 do // first 8 colors must not be changed in Excel
palette[i] := random(256) + random(256) shr 8 + random(256) shr 16;
end;
else palette.AddBuiltinColors;
end;
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorkSheet:= MyWorkBook.AddWorksheet(ColorsSheet); MyWorkSheet:= MyWorkBook.AddWorksheet(ColorsSheet);
// Define palette
case whichPalette of
5: MyWorkbook.UsePalette(@PALETTE_BIFF5, High(PALETTE_BIFF5)+1);
8: MyWorkbook.UsePalette(@PALETTE_BIFF8, High(PALETTE_BIFF8)+1);
999: begin // Random palette: testing of color replacement
MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
for i:=8 to 63 do // first 8 colors cannot be changed
MyWorkbook.SetPaletteColor(i, random(256) + random(256) shr 8 + random(256) shr 16);
end;
// else use default palette
end;
// Remember all colors because ODS does not have a palette in the file;
// therefore we do not know which colors to expect.
SetLength(pal, MyWorkbook.GetPaletteSize);
for color:=0 to High(pal) do
pal[color] := MyWorkbook.GetPaletteColor(color);
// Write out all colors // Write out all colors
row := 0; row := 0;
col := 0; col := 0;
for color := 0 to MyWorkbook.GetPaletteSize-1 do begin for i := 0 to palette.Count-1 do begin
MyWorksheet.WriteUTF8Text(row, col, CELLTEXT); MyWorksheet.WriteUTF8Text(row, col, CELLTEXT);
MyWorksheet.WriteFontColor(row, col, color); MyWorksheet.WriteFontColor(row, col, palette[i]);
MyCell := MyWorksheet.FindCell(row, col); MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then if MyCell = nil then
fail('Error in test code. Failed to get cell.'); fail('Error in test code. Failed to get cell.');
colorInFile := MyWorksheet.ReadCellFont(MyCell).Color; currentRGB := MyWorksheet.ReadCellFont(MyCell).Color;
currentRGB := MyWorkbook.GetPaletteColor(colorInFile); expectedRGB := palette[i];
expectedRGB := MyWorkbook.GetPaletteColor(color);
CheckEquals(expectedRGB, currentRGB, CheckEquals(expectedRGB, currentRGB,
'Test unsaved font color, cell ' + CellNotation(MyWorksheet,row, col)); 'Test unsaved font color, cell ' + CellNotation(MyWorksheet,row, col));
inc(row); inc(row);
@ -261,21 +256,18 @@ begin
MyWorksheet := GetWorksheetByName(MyWorkBook, ColorsSheet); MyWorksheet := GetWorksheetByName(MyWorkBook, ColorsSheet);
if MyWorksheet=nil then if MyWorksheet=nil then
fail('Error in test code. Failed to get named worksheet'); fail('Error in test code. Failed to get named worksheet');
col := 0;
for row := 0 to MyWorksheet.GetLastRowIndex do begin for row := 0 to MyWorksheet.GetLastRowIndex do begin
MyCell := MyWorksheet.FindCell(row, col); MyCell := MyWorksheet.FindCell(row, col);
if MyCell = nil then if MyCell = nil then
fail('Error in test code. Failed to get cell.'); fail('Error in test code. Failed to get cell.');
color := TsColor(row); expectedRGB := palette[row];
expectedRGB := pal[color]; currentRGB := MyWorksheet.ReadCellFont(MyCell).Color;
colorInFile := MyWorksheet.ReadCellFont(MyCell).Color;
currentRGB := MyWorkbook.GetPaletteColor(colorInFile);
// Excel2 cannot write the entire palette. The writer had called "FixColor". // Excel2 cannot write the entire palette. We have to look for the
// We simulate that here to get the color correct. // closest color.
if (AFormat = sfExcel2) and (color >= BIFF2_MAX_PALETTE_SIZE) then begin if (AFormat = sfExcel2) then
color := MyWorkbook.FindClosestColor(expectedRGB, BIFF2_MAX_PALETTE_SIZE); expectedRGB := palette[palette.FindClosestColorIndex(expectedRGB, BIFF2_MAX_PALETTE_SIZE)];
expectedRGB := MyWorkbook.GetPaletteColor(color);
end;
CheckEquals(expectedRGB, currentRGB, CheckEquals(expectedRGB, currentRGB,
'Test saved font color, cell '+CellNotation(MyWorksheet,Row,Col)); 'Test saved font color, cell '+CellNotation(MyWorksheet,Row,Col));
end; end;
@ -283,6 +275,10 @@ begin
MyWorkbook.Free; MyWorkbook.Free;
DeleteFile(TempFile); DeleteFile(TempFile);
end; end;
finally
palette.Free;
end;
end; end;
{ Tests for BIFF2 file format } { Tests for BIFF2 file format }

View File

@ -35,7 +35,7 @@ type
implementation implementation
uses uses
StrUtils, fpsRPN, xlsbiff5; StrUtils, fpsPalette, fpsRPN, xlsbiff5;
const const
ERROR_SHEET = 'ErrorTest'; //worksheet name ERROR_SHEET = 'ErrorTest'; //worksheet name
@ -67,6 +67,8 @@ var
ErrList: TStringList; ErrList: TStringList;
newColor: TsColor; newColor: TsColor;
expected: integer; expected: integer;
palette: TsPalette;
i: Integer;
begin begin
formula := '=A1'; formula := '=A1';
@ -122,14 +124,28 @@ begin
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
// Prepare a full palette // Prepare a full palette
MyWorkbook.UsePalette(@PALETTE_BIFF5[0], Length(PALETTE_BIFF5)); palette := TsPalette.Create;
// Add 1 more color - this is one too many for BIFF5 and 8, and a lot try
// too many for BIFF2 ! // Create random palette of 65 unique entries - 1 too many for Excel5/8
newColor := MyWorkbook.AddColorToPalette($FF7878); // and a lot too many for BIFF2
palette.AddBuiltinColors;
for i:=8 to 65 do
begin
repeat
newColor := random(256) + random(256) shl 8 + random(256) shl 16;
until palette.FindColor(newColor) = -1;
palette.AddColor(newColor);
end;
MyWorkSheet:= MyWorkBook.AddWorksheet(ERROR_SHEET); MyWorkSheet:= MyWorkBook.AddWorksheet(ERROR_SHEET);
MyWorksheet.WriteUTF8Text(0, 0, s);
MyWorksheet.WriteFontColor(0, 0, newColor); // Use all colors in order to have them in the palette to be written
// to file.
for row := 0 to palette.Count-1 do
begin
MyWorksheet.WriteUTF8Text(row, 0, s);
MyWorksheet.WriteFontColor(row, 0, palette[row]);
end;
TempFile:=NewTempFile; TempFile:=NewTempFile;
MyWorkBook.WriteToFile(TempFile, AFormat, true); MyWorkBook.WriteToFile(TempFile, AFormat, true);
@ -141,6 +157,11 @@ begin
// no palette in xml --> no error expected // no palette in xml --> no error expected
expected := 0; expected := 0;
CheckEquals(expected, ErrList.Count, 'Error count mismatch in test 3'); CheckEquals(expected, ErrList.Count, 'Error count mismatch in test 3');
finally
palette.Free;
end;
finally finally
MyWorkbook.Free; MyWorkbook.Free;
DeleteFile(TempFile); DeleteFile(TempFile);

View File

@ -156,7 +156,7 @@ type
implementation implementation
uses uses
TypInfo, fpsPatches, fpsutils, fpsnumformat, fpscsv; TypInfo, fpsPatches, fpsutils, fpsnumformat, fpspalette, fpscsv;
const const
FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet
@ -311,7 +311,7 @@ begin
SollBorderLineStyles[5] := lsDouble; SollBorderLineStyles[5] := lsDouble;
SollBorderLineStyles[6] := lsHair; SollBorderLineStyles[6] := lsHair;
SollBorderColors[0] := scBlue; SollBorderColors[0] := scBlack;
SollBorderColors[1] := scRed; SollBorderColors[1] := scRed;
SollBorderColors[2] := scBlue; SollBorderColors[2] := scBlue;
SollBorderColors[3] := scGray; SollBorderColors[3] := scGray;
@ -728,7 +728,6 @@ begin
// Write out all test values // Write out all test values
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
MyWorkSheet:= MyWorkBook.AddWorksheet(BackgroundSheet); MyWorkSheet:= MyWorkBook.AddWorksheet(BackgroundSheet);
for style in TsFillStyle do begin for style in TsFillStyle do begin
row := ord(style); row := ord(style);
@ -752,7 +751,6 @@ begin
MyWorksheet := GetWorksheetByName(MyWorkBook, BackgroundSheet); MyWorksheet := GetWorksheetByName(MyWorkBook, BackgroundSheet);
if MyWorksheet=nil then if MyWorksheet=nil then
fail('Error in test code. Failed to get named worksheet'); fail('Error in test code. Failed to get named worksheet');
for style in TsFillStyle do begin for style in TsFillStyle do begin
row := ord(style); row := ord(style);
@ -770,13 +768,13 @@ begin
begin begin
if PATTERN_COLOR <> patt.FgColor then if PATTERN_COLOR <> patt.FgColor then
CheckEquals( CheckEquals(
MyWorkbook.GetColorName(PATTERN_COLOR), GetColorName(PATTERN_COLOR),
MyWorkbook.GetColorName(patt.FgColor), GetColorName(patt.FgColor),
'Test saved fill pattern color mismatch, cell ' + CellNotation(MyWorksheet, row, col)); 'Test saved fill pattern color mismatch, cell ' + CellNotation(MyWorksheet, row, col));
if BK_COLOR <> patt.BgColor then if BK_COLOR <> patt.BgColor then
CheckEquals( CheckEquals(
MyWorkbook.GetColorName(BK_COLOR), GetColorName(BK_COLOR),
MyWorkbook.GetColorName(patt.BgColor), GetColorName(patt.BgColor),
'Test saved fill background color mismatch, cell ' + CellNotation(MyWorksheet, row, col)); 'Test saved fill background color mismatch, cell ' + CellNotation(MyWorksheet, row, col));
end; end;
@ -794,20 +792,20 @@ begin
begin begin
if PATTERN_COLOR <> patt.FgColor then if PATTERN_COLOR <> patt.FgColor then
CheckEquals( CheckEquals(
MyWorkbook.GetColorName(PATTERN_COLOR), GetColorName(PATTERN_COLOR),
MyWorkbook.GetColorName(patt.FgColor), GetColorName(patt.FgColor),
'Test saved fill pattern color mismatch, cell ' + CellNotation(MyWorksheet, row, col)); 'Test saved fill pattern color mismatch, cell ' + CellNotation(MyWorksheet, row, col));
// SolidFill is a special case: here the background color is always equal // SolidFill is a special case: here the background color is always equal
// to the pattern color - the cell layout does not know this... // to the pattern color - the cell layout does not know this...
if style = fsSolidFill then if style = fsSolidFill then
CheckEquals( CheckEquals(
MyWorkbook.GetColorName(PATTERN_COLOR), GetColorName(PATTERN_COLOR),
MyWorkbook.GetColorName(patt.BgColor), GetColorName(patt.BgColor),
'Test saved fill pattern color mismatch, cell ' + CellNotation(MyWorksheet, row, col)) 'Test saved fill pattern color mismatch, cell ' + CellNotation(MyWorksheet, row, col))
else else
CheckEquals( CheckEquals(
MyWorkbook.GetColorName(scTransparent), GetColorName(scTransparent),
MyWorkbook.GetColorName(patt.BgColor), GetColorName(patt.BgColor),
'Test saved fill background color mismatch, cell ' + CellNotation(MyWorksheet, row, col)); 'Test saved fill background color mismatch, cell ' + CellNotation(MyWorksheet, row, col));
end; end;
end; end;
@ -983,11 +981,11 @@ begin
begin begin
for col := 1 to 10 do for col := 1 to 10 do
begin begin
MyWorksheet.WriteBorders(row*2, col*2, borders); MyWorksheet.WriteBorders(row*2-1, col*2-1, borders);
for b in borders do for b in borders do
begin begin
MyWorksheet.WriteBorderLineStyle(row*2, col*2, b, SollBorderLineStyles[ls]); MyWorksheet.WriteBorderLineStyle(row*2-1, col*2-1, b, SollBorderLineStyles[ls]);
MyWorksheet.WriteBorderColor(row*2, col*2, b, SollBorderColors[c]); MyWorksheet.WriteBorderColor(row*2-1, col*2-1, b, SollBorderColors[c]);
inc(ls); inc(ls);
if ls > High(SollBorderLineStyles) then if ls > High(SollBorderLineStyles) then
begin begin
@ -1021,7 +1019,7 @@ begin
begin begin
for col := 1 to 10 do for col := 1 to 10 do
begin begin
MyCell := MyWorksheet.FindCell(row*2, col*2); MyCell := MyWorksheet.FindCell(row*2-1, col*2-1);
if myCell = nil then if myCell = nil then
fail('Error in test code. Failed to get cell.'); fail('Error in test code. Failed to get cell.');
for b in borders do for b in borders do
@ -1035,7 +1033,7 @@ begin
if AFormat in [sfExcel8, sfOOXML] then if AFormat in [sfExcel8, sfOOXML] then
case b of case b of
cbDiagUp : diagUp_ls := expected; cbDiagUp : diagUp_ls := expected;
cbDiagDown: expected := diagUp_ls; cbDiagDown : expected := diagUp_ls;
end; end;
CheckEquals(expected, current, CheckEquals(expected, current,
'Test saved border line style mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2)); 'Test saved border line style mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2));
@ -1047,7 +1045,7 @@ begin
if AFormat in [sfExcel8, sfOOXML] then if AFormat in [sfExcel8, sfOOXML] then
case b of case b of
cbDiagUp : diagUp_clr := expected; cbDiagUp : diagUp_clr := expected;
cbDiagDown: expected := diagUp_clr; cbDiagDown : expected := diagUp_clr;
end; end;
CheckEquals(expected, current, CheckEquals(expected, current,
'Test saved border color mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2)); 'Test saved border color mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2));
@ -1591,7 +1589,12 @@ var
fnt: TsFont; fnt: TsFont;
actual, expected: String; actual, expected: String;
i: Integer; i: Integer;
palette: TsPalette;
begin begin
palette := TsPalette.Create;
try
palette.AddBuiltinColors;
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorksheet:= MyWorkBook.AddWorksheet(SHEETNAME); MyWorksheet:= MyWorkBook.AddWorksheet(SHEETNAME);
@ -1600,7 +1603,7 @@ begin
begin begin
MyWorksheet.WriteNumber(r, c, 123); MyWorksheet.WriteNumber(r, c, 123);
MyWorksheet.WriteBackgroundColor(r, c, 0); MyWorksheet.WriteBackgroundColor(r, c, 0);
MyWorksheet.WriteFont(r, c, 'Times New Roman', FontSizes[r], [], c); // Biff2 has only 8 colors --> re-use the black! MyWorksheet.WriteFont(r, c, 'Times New Roman', FontSizes[r], [], palette[c]); // Biff2 has only 8 colors --> re-use the black!
// --> in total 64 combinations // --> in total 64 combinations
end; end;
TempFile:=NewTempFile; TempFile:=NewTempFile;
@ -1631,7 +1634,7 @@ begin
actual := FloatToStr(fnt.Size); actual := FloatToStr(fnt.Size);
CheckEquals(expected, actual, CheckEquals(expected, actual,
'Font size mismatch, cell '+ CellNotation(MyWorksheet, r, c)); 'Font size mismatch, cell '+ CellNotation(MyWorksheet, r, c));
expected := IntToStr(c); expected := IntToStr(palette[c]);
actual := IntToStr(fnt.Color); actual := IntToStr(fnt.Color);
CheckEquals(expected, actual, CheckEquals(expected, actual,
'Font color mismatch, cell '+ CellNotation(MyWorksheet, r, c)); 'Font color mismatch, cell '+ CellNotation(MyWorksheet, r, c));
@ -1641,6 +1644,10 @@ begin
MyWorkbook.Free; MyWorkbook.Free;
DeleteFile(TempFile); DeleteFile(TempFile);
end; end;
finally
palette.Free;
end;
end; end;

View File

@ -63,17 +63,20 @@ type
{$ENDIF} {$ENDIF}
// For BIFF8 format, writes all background colors in A1..A16 // For BIFF8 format, writes all background colors in A1..A16
procedure TestBiff8CellBackgroundColor; procedure TestBiff8CellBackgroundColor;
procedure TestNumberFormats;
end; end;
implementation implementation
uses uses
fpstypes, fpsUtils, rpnFormulaUnit; fpstypes, fpsUtils, fpsPalette, rpnFormulaUnit;
const const
COLORSHEETNAME='color_sheet'; //for background color tests COLORSHEETNAME='color_sheet'; //for background color tests
RPNSHEETNAME='rpn_formula_sheet'; //for rpn formula tests RPNSHEETNAME='rpn_formula_sheet'; //for rpn formula tests
FORMULASHEETNAME='formula_sheet'; // for string formula tests FORMULASHEETNAME='formula_sheet'; // for string formula tests
NUMBERFORMATSHEETNAME='number format sheet'; // for number format tests
OUTPUT_FORMAT = sfExcel8; //change manually if you want to test different formats. To do: automatically output all formats OUTPUT_FORMAT = sfExcel8; //change manually if you want to test different formats. To do: automatically output all formats
var var
@ -184,6 +187,7 @@ var
Cell : PCell; Cell : PCell;
i: cardinal; i: cardinal;
RowOffset: cardinal; RowOffset: cardinal;
palette: TsPalette;
begin begin
if OUTPUT_FORMAT <> sfExcel8 then if OUTPUT_FORMAT <> sfExcel8 then
Ignore('This test only applies to BIFF8 XLS output format.'); Ignore('This test only applies to BIFF8 XLS output format.');
@ -196,15 +200,74 @@ begin
if Workbook = nil then if Workbook = nil then
Workbook := TsWorkbook.Create; Workbook := TsWorkbook.Create;
palette := TsPalette.Create;
try
palette.AddBuiltinColors;
palette.AddExcelColors;
Worksheet := Workbook.AddWorksheet(COLORSHEETNAME); Worksheet := Workbook.AddWorksheet(COLORSHEETNAME);
WorkSheet.WriteUTF8Text(0,1,'TSpreadManualTests.TestBiff8CellBackgroundColor'); WorkSheet.WriteUTF8Text(0, 1, 'TSpreadManualTests.TestBiff8CellBackgroundColor');
RowOffset := 1; RowOffset := 1;
for i:=0 to Workbook.GetPaletteSize-1 do begin for i:=0 to palette.Count-1 do begin
WorkSheet.WriteUTF8Text(i+RowOffset,0,'BACKGROUND COLOR TEST'); cell := WorkSheet.WriteUTF8Text(i+RowOffset,0,'BACKGROUND COLOR TEST');
Cell := Worksheet.GetCell(i+RowOffset, 0); Worksheet.WriteBackgroundColor(Cell, palette[i]);
Worksheet.WriteBackgroundColor(Cell, TsColor(i)); Worksheet.WriteFontColor(cell, HighContrastColor(palette[i]));
WorkSheet.WriteUTF8Text(i+RowOffset,1,'Cell to the left should be '+Workbook.GetColorName(i)+'. Please check.'); WorkSheet.WriteUTF8Text(i+RowOffset,1,'Cell to the left should be '+GetColorName(palette[i])+'. Please check.');
end; end;
Worksheet.WriteColWidth(0, 30);
Worksheet.WriteColWidth(1, 60);
finally
palette.Free;
end;
end;
procedure TSpreadManualTests.TestNumberFormats();
// source: forum post
// http://forum.lazarus.freepascal.org/index.php/topic,19887.msg134114.html#msg134114
// possible fix for values there too
const
Values: Array[0..4] of Double = (12000.34, -12000.34, 0.0001234, -0.0001234, 0.0);
FormatStrings: array[0..24] of String = (
'General',
'0', '0.00', '0.0000',
'#,##0', '#,##0.00', '#,##0.0000',
'0%', '0.00%', '0.0000%',
'0,', '0.00,', '0.0000,',
'0E+00', '0.00E+00', '0.0000E+00',
'0E-00', '0.00E-00', '0.0000E-00',
'# ?/?', '# ??/??', '# ????/????',
'?/?', '??/??', '????/????'
);
var
Worksheet: TsWorksheet;
Cell : PCell;
i: cardinal;
r, c: Cardinal;
palette: TsPalette;
nfs: String;
begin
if OUTPUT_FORMAT <> sfExcel8 then
Ignore('This test only applies to BIFF8 XLS output format.');
// No worksheets in BIFF2. Since main interest is here in formulas we just jump
// off here - need to change this in the future...
if OUTPUT_FORMAT = sfExcel2 then
Ignore('BIFF2 does not support worksheets. Ignoring manual tests for now');
if Workbook = nil then
Workbook := TsWorkbook.Create;
Worksheet := Workbook.AddWorksheet(NUMBERFORMATSHEETNAME);
WorkSheet.WriteUTF8Text(0, 1, 'Number format tests');
for r:=0 to High(FormatStrings) do
begin
Worksheet.WriteUTF8Text(r+2, 0, FormatStrings[r]);
for c:=0 to High(Values) do
Worksheet.WriteNumber(r+2, c+1, values[c], nfCustom, FormatStrings[r]);
end;
Worksheet.WriteColWidth(0, 20);
end; end;
{$IFDEF FPSPREAD_HAS_NEWRPNSUPPORT} {$IFDEF FPSPREAD_HAS_NEWRPNSUPPORT}

View File

@ -270,7 +270,8 @@ begin
if copy(lFormatstr, 1, 6) = 'color:' then if copy(lFormatstr, 1, 6) = 'color:' then
begin begin
lColorstr := Copy(lFormatstr, 7, Length(lFormatStr)); lColorstr := Copy(lFormatstr, 7, Length(lFormatStr));
lCurBackgroundColor := FWorkbook.AddColorToPalette(HTMLColorStrToColor(lColorStr)); lCurBackgroundColor := HTMLColorStrToColor(lColorStr);
// lCurBackgroundColor := FWorkbook.AddColorToPalette(HTMLColorStrToColor(lColorStr));
lUseBackgroundColor := True; lUseBackgroundColor := True;
lFormatStr := ''; lFormatStr := '';
end; end;
@ -381,7 +382,8 @@ procedure TsWikiTableWriter.WriteToStrings_WikiMedia(AStrings: TStrings);
clr := fmt^.BorderStyles[ABorder].Color; clr := fmt^.BorderStyles[ABorder].Color;
Result := Format('border-%s:%s', [BORDERNAMES[ABorder], LINESTYLES[ls]]); Result := Format('border-%s:%s', [BORDERNAMES[ABorder], LINESTYLES[ls]]);
if clr <> scBlack then if clr <> scBlack then
Result := Result + ' ' + FWorkbook.GetPaletteColorAsHTMLStr(clr) + '; '; Result := Result + ' ' + ColorToHTMLColorStr(clr) + '; ';
// Result := Result + ' ' + FWorkbook.GetPaletteColorAsHTMLStr(clr) + '; ';
end; end;
const const
@ -499,8 +501,10 @@ begin
begin begin
lCurColor := FWorksheet.ReadBackgroundColor(lCell); lCurColor := FWorksheet.ReadBackgroundColor(lCell);
lStyleStr := Format('background-color:%s;color:%s;', [ lStyleStr := Format('background-color:%s;color:%s;', [
FWorkbook.GetPaletteColorAsHTMLStr(lCurColor), ColorToHTMLColorStr(lCurColor),
FWorkbook.GetPaletteColorAsHTMLStr(lFont.Color) ColorToHTMLColorStr(lFont.Color)
// FWorkbook.GetPaletteColorAsHTMLStr(lCurColor),
// FWorkbook.GetPaletteColorAsHTMLStr(lFont.Color)
]); ]);
end; end;

View File

@ -144,7 +144,7 @@ var
var var
{ the palette of the default BIFF2 colors as "big-endian color" values } { the palette of the default BIFF2 colors as "big-endian color" values }
PALETTE_BIFF2: array[$0..$07] of TsColorValue = ( PALETTE_BIFF2: array[$0..$07] of TsColor = (
$000000, // $00: black $000000, // $00: black
$FFFFFF, // $01: white $FFFFFF, // $01: white
$FF0000, // $02: red $FF0000, // $02: red
@ -159,7 +159,7 @@ var
implementation implementation
uses uses
Math, fpsStrings, fpsReaderWriter; Math, fpsStrings, fpsReaderWriter, fpsPalette;
const const
{ Excel record IDs } { Excel record IDs }
@ -419,8 +419,12 @@ begin
end; end;
procedure TsSpreadBIFF2Reader.ReadFONTCOLOR(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadFONTCOLOR(AStream: TStream);
var
lColor: Word;
begin begin
FFont.Color := WordLEToN(AStream.ReadWord); lColor := WordLEToN(AStream.ReadWord); // Palette index
FFont.Color := IfThen(lColor = SYS_DEFAULT_WINDOW_TEXT_COLOR,
scBlack, FPalette[lColor]);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -1533,7 +1537,7 @@ begin
AStream.WriteWord(WordToLE(2)); AStream.WriteWord(WordToLE(2));
{ Font color index, only first 8 palette entries allowed! } { Font color index, only first 8 palette entries allowed! }
AStream.WriteWord(WordToLE(word(FixColor(font.Color)))); AStream.WriteWord(WordToLE(PaletteIndex(font.Color)));
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -1987,6 +1991,6 @@ initialization
{$ENDIF} {$ENDIF}
RegisterSpreadFormat(TsSpreadBIFF2Reader, TsSpreadBIFF2Writer, sfExcel2); RegisterSpreadFormat(TsSpreadBIFF2Reader, TsSpreadBIFF2Writer, sfExcel2);
MakeLEPalette(@PALETTE_BIFF2, Length(PALETTE_BIFF2)); MakeLEPalette(PALETTE_BIFF2);
end. end.

View File

@ -77,6 +77,7 @@ type
FWorksheetNames: TStringList; FWorksheetNames: TStringList;
FCurrentWorksheet: Integer; FCurrentWorksheet: Integer;
protected protected
procedure PopulatePalette; override;
{ Record writing methods } { Record writing methods }
procedure ReadBoundsheet(AStream: TStream); procedure ReadBoundsheet(AStream: TStream);
procedure ReadFONT(const AStream: TStream); procedure ReadFONT(const AStream: TStream);
@ -137,7 +138,7 @@ var
var var
// the palette of the default BIFF5 colors as "big-endian color" values // the palette of the default BIFF5 colors as "big-endian color" values
PALETTE_BIFF5: array[$00..$3F] of TsColorValue = ( PALETTE_BIFF5: array[$00..$3F] of TsColor = (
$000000, // $00: black $000000, // $00: black
$FFFFFF, // $01: white $FFFFFF, // $01: white
$FF0000, // $02: red $FF0000, // $02: red
@ -213,7 +214,7 @@ var
implementation implementation
uses uses
fpsStrings, fpsStreams, fpsReaderWriter; Math, fpsStrings, fpsStreams, fpsReaderWriter, fpsPalette;
const const
{ Excel record IDs } { Excel record IDs }
@ -337,6 +338,15 @@ type
{ TsSpreadBIFF5Reader } { TsSpreadBIFF5Reader }
{@@ ----------------------------------------------------------------------------
Populates the reader's default palette using the BIFF5 default colors.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF5Reader.PopulatePalette;
begin
FPalette.Clear;
FPalette.UseColors(PALETTE_BIFF5);
end;
procedure TsSpreadBIFF5Reader.ReadWorkbookGlobals(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadWorkbookGlobals(AStream: TStream);
var var
SectionEOF: Boolean = False; SectionEOF: Boolean = False;
@ -370,6 +380,9 @@ begin
// Check for the end of the file // Check for the end of the file
if AStream.Position >= AStream.Size then SectionEOF := True; if AStream.Position >= AStream.Size then SectionEOF := True;
end; end;
// Convert palette indexes to rgb colors
FixColors;
end; end;
procedure TsSpreadBIFF5Reader.ReadWorksheet(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadWorksheet(AStream: TStream);
@ -606,8 +619,7 @@ procedure TsSpreadBIFF5Reader.ReadXF(AStream: TStream);
var var
rec: TBIFF5_XFRecord; rec: TBIFF5_XFRecord;
fmt: TsCellFormat; fmt: TsCellFormat;
// nfidx: Integer; i, cidx: Integer;
i: Integer;
nfparams: TsNumFormatParams; nfparams: TsNumFormatParams;
nfs: String; nfs: String;
b: Byte; b: Byte;
@ -646,28 +658,7 @@ begin
Include(fmt.UsedFormattingFields, uffNumberFormat); Include(fmt.UsedFormattingFields, uffNumberFormat);
end; end;
end; end;
{
// Number format index
nfparams := Workbook.GetNumberFormat(rec.NumFormatIndex);
nfs := nfParams.NumFormatStr[nfdDefault];
if nfs <> '' then begin
fmt.NumberFormatIndex := Workbook.AddNumberFormat(nfs);
fmt.NumberFormat := nfParams.NumFormat;
fmt.NumberFormatStr := nfs;
Include(fmt.UsedFormattingFields, uffNumberFormat);
end;
}
{
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 // Horizontal text alignment
b := rec.Align_TextBreak AND MASK_XF_HOR_ALIGN; b := rec.Align_TextBreak AND MASK_XF_HOR_ALIGN;
if (b <= ord(High(TsHorAlignment))) then if (b <= ord(High(TsHorAlignment))) then
@ -742,10 +733,17 @@ begin
end; end;
// Border line colors // Border line colors
fmt.BorderStyles[cbWest].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_LEFT_COLOR) shr 16; // NOTE: It is possible that the palette is not yet known at this moment.
fmt.BorderStyles[cbEast].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_RIGHT_COLOR) shr 23; // Therefore we store the palette index encoded into the colors.
fmt.BorderStyles[cbNorth].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_TOP_COLOR) shr 9; // They will be converted to rgb in "FixColors".
fmt.BorderStyles[cbSouth].Color := (rec.Border_BkGr1 and MASK_XF_BORDER_BOTTOM_COLOR) shr 25; cidx := (rec.Border_BkGr2 and MASK_XF_BORDER_LEFT_COLOR) shr 16;
fmt.BorderStyles[cbWest].Color := IfThen(cidx >= 64, scBlack, SetAsPaletteIndex(cidx));
cidx := (rec.Border_BkGr2 and MASK_XF_BORDER_RIGHT_COLOR) shr 23;
fmt.BorderStyles[cbEast].Color := IfThen(cidx >= 64, scBlack, SetAsPaletteIndex(cidx));
cidx := (rec.Border_BkGr2 and MASK_XF_BORDER_TOP_COLOR) shr 9;
fmt.BorderStyles[cbNorth].Color := IfThen(cidx >= 64, scBlack, SetAsPaletteIndex(cidx));
cidx := (rec.Border_BkGr1 and MASK_XF_BORDER_BOTTOM_COLOR) shr 25;
fmt.BorderStyles[cbSouth].Color := IfThen(cidx >= 64, scBlack, SetAsPaletteIndex(cidx));
// Background // Background
fill := (rec.Border_BkGr1 and MASK_XF_BKGR_FILLPATTERN) shr 16; fill := (rec.Border_BkGr1 and MASK_XF_BKGR_FILLPATTERN) shr 16;
@ -758,12 +756,12 @@ begin
// Fill style // Fill style
fmt.Background.Style := fs; fmt.Background.Style := fs;
// Pattern color // Pattern color
fmt.Background.FgColor := rec.Border_BkGr1 and MASK_XF_BKGR_PATTERN_COLOR; cidx := rec.Border_BkGr1 and MASK_XF_BKGR_PATTERN_COLOR; // Palette index
if fmt.Background.FgColor = SYS_DEFAULT_FOREGROUND_COLOR then fmt.Background.FgColor := IfThen(cidx = SYS_DEFAULT_FOREGROUND_COLOR,
fmt.Background.FgColor := scBlack; scBlack, SetAsPaletteIndex(cidx));
fmt.Background.BgColor := (rec.Border_BkGr1 and MASK_XF_BKGR_BACKGROUND_COLOR) shr 7; cidx := (rec.Border_BkGr1 and MASK_XF_BKGR_BACKGROUND_COLOR) shr 7;
if fmt.Background.BgColor = SYS_DEFAULT_BACKGROUND_COLOR then fmt.Background.BgColor := IfThen(cidx = SYS_DEFAULT_BACKGROUND_COLOR,
fmt.Background.BgColor := scTransparent; scTransparent, SetAsPaletteIndex(cidx));
Include(fmt.UsedFormattingFields, uffBackground); Include(fmt.UsedFormattingFields, uffBackground);
break; break;
end; end;
@ -785,14 +783,12 @@ begin
BIFF5EOF := False; BIFF5EOF := False;
{ Read workbook globals } { Read workbook globals }
ReadWorkbookGlobals(AStream); ReadWorkbookGlobals(AStream);
// Check for the end of the file { Check for the end of the file }
if AStream.Position >= AStream.Size then BIFF5EOF := True; if AStream.Position >= AStream.Size then BIFF5EOF := True;
{ Now read all worksheets } { Now read all worksheets }
while (not BIFF5EOF) do while (not BIFF5EOF) do
begin begin
ReadWorksheet(AStream); ReadWorksheet(AStream);
@ -807,11 +803,7 @@ begin
// at the end of the file. // at the end of the file.
end; end;
if not FPaletteFound then { Finalization }
FWorkbook.UsePalette(@PALETTE_BIFF5, Length(PALETTE_BIFF5));
{ Finalizations }
FWorksheetNames.Free; FWorksheetNames.Free;
end; end;
@ -840,10 +832,21 @@ begin
if lOptions and $0004 <> 0 then Include(font.Style, fssUnderline); if lOptions and $0004 <> 0 then Include(font.Style, fssUnderline);
if lOptions and $0008 <> 0 then Include(font.Style, fssStrikeout); if lOptions and $0008 <> 0 then Include(font.Style, fssStrikeout);
{ Colour index } { Color index }
// The problem is that the palette is loaded after the font list; therefore
// we do not know the rgb color of the font here. We store the palette index
// ("SetAsPaletteIndex") and replace it by the rgb color at the end of the
// workbook globals records. As an indicator that the font does not yet
// contain an rgb color a control bit is set in the high-byte of the TsColor.
lColor := WordLEToN(AStream.ReadWord); lColor := WordLEToN(AStream.ReadWord);
//font.Color := TsColor(lColor - 8); // Palette colors have an offset 8 if lColor < 8 then
font.Color := tsColor(lColor); // Use built-in colors directly otherwise the Workbook's FindFont would not find the font in ReadXF
font.Color := FPalette[lColor]
else
if lColor = SYS_DEFAULT_WINDOW_TEXT_COLOR then
font.Color := scBlack
else
font.Color := SetAsPaletteIndex(lColor);
{ Font weight } { Font weight }
lWeight := WordLEToN(AStream.ReadWord); lWeight := WordLEToN(AStream.ReadWord);
@ -1197,6 +1200,7 @@ procedure TsSpreadBIFF5Writer.WriteFont(AStream: TStream; AFont: TsFont);
var var
Len: Byte; Len: Byte;
optn: Word; optn: Word;
cidx: Integer;
begin begin
if AFont = nil then // this happens for FONT4 in case of BIFF if AFont = nil then // this happens for FONT4 in case of BIFF
exit; exit;
@ -1222,8 +1226,8 @@ begin
if fssStrikeout in AFont.Style then optn := optn or $0008; if fssStrikeout in AFont.Style then optn := optn or $0008;
AStream.WriteWord(WordToLE(optn)); AStream.WriteWord(WordToLE(optn));
{ Colour index } { Color index }
AStream.WriteWord(WordToLE(ord(FixColor(AFont.Color)))); AStream.WriteWord(WordToLE(PaletteIndex(AFont.Color)));
{ Font weight } { Font weight }
if fssBold in AFont.Style then if fssBold in AFont.Style then
@ -1574,19 +1578,19 @@ begin
begin begin
if (AFormatRecord^.Background.FgColor = scTransparent) if (AFormatRecord^.Background.FgColor = scTransparent)
then dw1 := dw1 or (SYS_DEFAULT_FOREGROUND_COLOR and $0000007F) then dw1 := dw1 or (SYS_DEFAULT_FOREGROUND_COLOR and $0000007F)
else dw1 := dw1 or (FixColor(AFormatRecord^.Background.FgColor) and $0000007F); else dw1 := dw1 or (PaletteIndex(AFormatRecord^.Background.FgColor) and $0000007F);
if AFormatRecord^.Background.BgColor = scTransparent if AFormatRecord^.Background.BgColor = scTransparent
then dw1 := dw1 or (SYS_DEFAULT_BACKGROUND_COLOR shl 7) then dw1 := dw1 or (SYS_DEFAULT_BACKGROUND_COLOR shl 7)
else dw1 := dw1 or (FixColor(AFormatRecord^.Background.BgColor) shl 7); else dw1 := dw1 or (PaletteIndex(AFormatRecord^.Background.BgColor) shl 7);
dw1 := dw1 or (MASK_XF_FILL_PATT[AFormatRecord^.Background.Style] shl 16); dw1 := dw1 or (MASK_XF_FILL_PATT[AFormatRecord^.Background.Style] shl 16);
end; end;
// Border lines // Border lines
if (uffBorder in AFormatRecord^.UsedFormattingFields) then if (uffBorder in AFormatRecord^.UsedFormattingFields) then
begin begin
dw1 := dw1 or (AFormatRecord^.BorderStyles[cbSouth].Color shl 25); // Bottom line color dw1 := dw1 or PaletteIndex(AFormatRecord^.BorderStyles[cbSouth].Color) shl 25; // Bottom line color
dw2 := (FixColor(AFormatRecord^.BorderStyles[cbNorth].Color) shl 9) or // Top line color dw2 := (PaletteIndex(AFormatRecord^.BorderStyles[cbNorth].Color) shl 9) or // Top line color
(FixColor(AFormatRecord^.BorderStyles[cbWest].Color) shl 16) or // Left line color (PaletteIndex(AFormatRecord^.BorderStyles[cbWest].Color) shl 16) or // Left line color
(FixColor(AFormatRecord^.BorderStyles[cbEast].Color) shl 23); // Right line color (PaletteIndex(AFormatRecord^.BorderStyles[cbEast].Color) shl 23); // Right line color
if cbSouth in AFormatRecord^.Border then if cbSouth in AFormatRecord^.Border then
dw1 := dw1 or ((DWord(AFormatRecord^.BorderStyles[cbSouth].LineStyle)+1) shl 22); dw1 := dw1 or ((DWord(AFormatRecord^.BorderStyles[cbSouth].LineStyle)+1) shl 22);
if cbNorth in AFormatRecord^.Border then if cbNorth in AFormatRecord^.Border then
@ -1612,7 +1616,7 @@ initialization
{$ENDIF} {$ENDIF}
RegisterSpreadFormat(TsSpreadBIFF5Reader, TsSpreadBIFF5Writer, sfExcel5); RegisterSpreadFormat(TsSpreadBIFF5Reader, TsSpreadBIFF5Writer, sfExcel5);
MakeLEPalette(@PALETTE_BIFF5, Length(PALETTE_BIFF5)); MakeLEPalette(PALETTE_BIFF5);
end. end.

View File

@ -84,6 +84,7 @@ type
procedure ReadBoundsheet(AStream: TStream); procedure ReadBoundsheet(AStream: TStream);
function ReadString(const AStream: TStream; const ALength: WORD): String; function ReadString(const AStream: TStream; const ALength: WORD): String;
protected protected
procedure PopulatePalette; override;
procedure ReadCONTINUE(const AStream: TStream); procedure ReadCONTINUE(const AStream: TStream);
procedure ReadFONT(const AStream: TStream); procedure ReadFONT(const AStream: TStream);
procedure ReadFORMAT(AStream: TStream); override; procedure ReadFORMAT(AStream: TStream); override;
@ -181,7 +182,7 @@ var
var var
// the palette of the 64 default BIFF8 colors as "big-endian color" values // the palette of the 64 default BIFF8 colors as "big-endian color" values
PALETTE_BIFF8: array[$00..$3F] of TsColorValue = ( PALETTE_BIFF8: array[$00..$3F] of TsColor = (
$000000, // $00: black // 8 built-in default colors $000000, // $00: black // 8 built-in default colors
$FFFFFF, // $01: white $FFFFFF, // $01: white
$FF0000, // $02: red $FF0000, // $02: red
@ -191,72 +192,73 @@ var
$FF00FF, // $06: magenta $FF00FF, // $06: magenta
$00FFFF, // $07: cyan $00FFFF, // $07: cyan
$000000, // $08: EGA black $000000, // $08: EGA black 1
$FFFFFF, // $09: EGA white $FFFFFF, // $09: EGA white 2
$FF0000, // $0A: EGA red $FF0000, // $0A: EGA red 3
$00FF00, // $0B: EGA green $00FF00, // $0B: EGA green 4
$0000FF, // $0C: EGA blue $0000FF, // $0C: EGA blue 5
$FFFF00, // $0D: EGA yellow $FFFF00, // $0D: EGA yellow 6
$FF00FF, // $0E: EGA magenta $FF00FF, // $0E: EGA magenta 7 pink
$00FFFF, // $0F: EGA cyan $00FFFF, // $0F: EGA cyan 8 turqoise
$800000, // $10: EGA dark red $800000, // $10=16: EGA dark red 9
$008000, // $11: EGA dark green $008000, // $11=17: EGA dark green 10
$000080, // $12: EGA dark blue $000080, // $12=18: EGA dark blue 11
$808000, // $13: EGA olive $808000, // $13=19: EGA olive 12 dark yellow
$800080, // $14: EGA purple $800080, // $14=20: EGA purple 13 violet
$008080, // $15: EGA teal $008080, // $15=21: EGA teal 14
$C0C0C0, // $16: EGA silver $C0C0C0, // $16=22: EGA silver 15 gray 25%
$808080, // $17: EGA gray $808080, // $17=23: EGA gray 16 gray 50%
$9999FF, // $18: $9999FF, // $18=24: Periwinkle
$993366, // $19: $993366, // $19=25: Plum
$FFFFCC, // $1A: $FFFFCC, // $1A=26: Ivory
$CCFFFF, // $1B: $CCFFFF, // $1B=27: Light turquoise
$660066, // $1C: $660066, // $1C=28: Dark purple
$FF8080, // $1D: $FF8080, // $1D=29: Coral
$0066CC, // $1E: $0066CC, // $1E=30: Ocean blue
$CCCCFF, // $1F: $CCCCFF, // $1F=31: Ice blue
$000080, // $20: $000080, // $20=32: Navy (repeated)
$FF00FF, // $21: $FF00FF, // $21=33: Pink (magenta repeated)
$FFFF00, // $22: $FFFF00, // $22=34: Yellow (repeated)
$00FFFF, // $23: $00FFFF, // $23=35: Turqoise (=cyan repeated)
$800080, // $24: $800080, // $24=36: Purple (repeated)
$800000, // $25: $800000, // $25=37: Dark red (repeated)
$008080, // $26: $008080, // $26=38: Teal (repeated)
$0000FF, // $27: $0000FF, // $27=39: Blue (repeated)
$00CCFF, // $28: $00CCFF, // $28=40: Sky blue
$CCFFFF, // $29: $CCFFFF, // $29=41: Light turquoise (repeated)
$CCFFCC, // $2A: $CCFFCC, // $2A=42: Light green
$FFFF99, // $2B: $FFFF99, // $2B=43: Light yellow
$99CCFF, // $2C: $99CCFF, // $2C=44: Pale blue
$FF99CC, // $2D: $FF99CC, // $2D=45: rose
$CC99FF, // $2E: $CC99FF, // $2E=46: lavander
$FFCC99, // $2F: $FFCC99, // $2F=47: tan
$3366FF, // $30: $3366FF, // $30=48: Light blue
$33CCCC, // $31: $33CCCC, // $31=49: Aqua
$99CC00, // $32: $99CC00, // $32=50: Lime
$FFCC00, // $33: $FFCC00, // $33=51: Gold
$FF9900, // $34: $FF9900, // $34=52: Light orange
$FF6600, // $35: $FF6600, // $35=53: Orange
$666699, // $36: $666699, // $36=54: Blue gray
$969696, // $37: $969696, // $37=55: Gray 40%
$003366, // $38: $003366, // $38=56: Dark teal
$339966, // $39: $339966, // $39=57: Sea green
$003300, // $3A: $003300, // $3A=58: very dark green
$333300, // $3B: $333300, // $3B=59: olive green
$993300, // $3C: $993300, // $3C=60: brown
$993366, // $3D: $993366, // $3D=61: plum
$333399, // $3E: $333399, // $3E=62: indigo
$333333 // $3F: $333333 // $3F=63: gray 80%
); );
// color names according to http://dmcritchie.mvps.org/EXCEL/COLORS.HTM
implementation implementation
uses uses
Math, lconvencoding, LazFileUtils, URIParser, Math, lconvencoding, LazFileUtils, URIParser,
fpsStrings, fpsStreams, fpsReaderWriter, fpsExprParser, xlsEscher; fpsStrings, fpsStreams, fpsReaderWriter, fpsPalette, fpsExprParser, xlsEscher;
const const
{ Excel record IDs } { Excel record IDs }
@ -429,10 +431,21 @@ begin
inherited; inherited;
end; end;
{ Reads a CONTINUE record. If the Flag "FCommentPending" is active then this {@@ ----------------------------------------------------------------------------
Populates the reader's default palette using the BIFF8 default colors.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Reader.PopulatePalette;
begin
FPalette.Clear;
FPalette.UseColors(PALETTE_BIFF8);
end;
{@@ ----------------------------------------------------------------------------
Reads a CONTINUE record. If the Flag "FCommentPending" is active then this
record contains the text of a comment assigned to a cell. The length of the record contains the text of a comment assigned to a cell. The length of the
string is taken from the preceeding TXO record, and the ID of the comment is string is taken from the preceeding TXO record, and the ID of the comment is
extracted in another preceeding record, an OBJ record. } extracted in another preceeding record, an OBJ record.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Reader.ReadCONTINUE(const AStream: TStream); procedure TsSpreadBIFF8Reader.ReadCONTINUE(const AStream: TStream);
var var
commentStr: String; commentStr: String;
@ -657,6 +670,9 @@ begin
// Check for the end of the file // Check for the end of the file
if AStream.Position >= AStream.Size then SectionEOF := True; if AStream.Position >= AStream.Size then SectionEOF := True;
end; end;
// Convert palette indexes to rgb colors
FixColors;
end; end;
procedure TsSpreadBIFF8Reader.ReadWorksheet(AStream: TStream); procedure TsSpreadBIFF8Reader.ReadWorksheet(AStream: TStream);
@ -839,9 +855,6 @@ begin
// at the end of the file. // at the end of the file.
end; end;
if not FPaletteFound then
FWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
{ Finalizations } { Finalizations }
FWorksheetNames.Free; FWorksheetNames.Free;
end; end;
@ -1206,6 +1219,7 @@ var
nfs: String; nfs: String;
nfParams: TsNumFormatParams; nfParams: TsNumFormatParams;
i: Integer; i: Integer;
iclr: Integer;
fnt: TsFont; fnt: TsFont;
begin begin
InitFormatRecord(fmt); InitFormatRecord(fmt);
@ -1326,29 +1340,39 @@ begin
end; end;
// Border line colors // Border line colors
fmt.BorderStyles[cbWest].Color := (rec.Border_BkGr1 and MASK_XF_BORDER_LEFT_COLOR) shr 16; // NOTE: It is possible that the palette is not yet known at this moment.
fmt.BorderStyles[cbEast].Color := (rec.Border_BkGr1 and MASK_XF_BORDER_RIGHT_COLOR) shr 23; // Therefore we store the palette index encoded into the colorx.
fmt.BorderStyles[cbNorth].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_TOP_COLOR); // They will be converted to rgb in "FixColors".
fmt.BorderStyles[cbSouth].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_BOTTOM_COLOR) shr 7; iclr := (rec.Border_BkGr1 and MASK_XF_BORDER_LEFT_COLOR) shr 16;
fmt.BorderStyles[cbDiagUp].Color := (rec.Border_BkGr2 and MASK_XF_BORDER_DIAGONAL_COLOR) shr 14; fmt.BorderStyles[cbWest].Color := IfThen(iclr >= 64, scBlack, SetAsPaletteIndex(iclr));
iclr := (rec.Border_BkGr1 and MASK_XF_BORDER_RIGHT_COLOR) shr 23;
fmt.BorderStyles[cbEast].Color := IfThen(iclr >= 64, scBlack, SetAsPaletteIndex(iclr));
iclr := (rec.Border_BkGr2 and MASK_XF_BORDER_TOP_COLOR);
fmt.BorderStyles[cbNorth].Color := IfThen(iclr >= 64, scBlack, SetAsPaletteIndex(iclr));
iclr := (rec.Border_BkGr2 and MASK_XF_BORDER_BOTTOM_COLOR) shr 7;
fmt.BorderStyles[cbSouth].Color := IfThen(iclr >= 64, scBlack, SetAsPaletteIndex(iclr));
iclr := (rec.Border_BkGr2 and MASK_XF_BORDER_DIAGONAL_COLOR) shr 14;
fmt.BorderStyles[cbDiagUp].Color := IfThen(iclr >= 64, scBlack, SetAsPaletteIndex(iclr));
fmt.BorderStyles[cbDiagDown].Color := fmt.BorderStyles[cbDiagUp].Color; fmt.BorderStyles[cbDiagDown].Color := fmt.BorderStyles[cbDiagUp].Color;
// Background fill pattern and color // Background fill pattern and color
fill := (rec.Border_BkGr2 and MASK_XF_BACKGROUND_PATTERN) shr 26; fill := (rec.Border_BkGr2 and MASK_XF_BACKGROUND_PATTERN) shr 26;
if fill <> MASK_XF_FILL_PATT_EMPTY then if fill <> MASK_XF_FILL_PATT_EMPTY then
begin begin
rec.BkGr3 := DWordLEToN(rec.BkGr3);
for fs in TsFillStyle do for fs in TsFillStyle do
if fill = MASK_XF_FILL_PATT[fs] then if fill = MASK_XF_FILL_PATT[fs] then
begin begin
rec.BkGr3 := DWordLEToN(rec.BkGr3);
// Pattern color // Pattern color
fmt.Background.FgColor := rec.BkGr3 and $007F; iclr := rec.BkGr3 and $007F;
if fmt.Background.FgColor = SYS_DEFAULT_FOREGROUND_COLOR then fmt.Background.FgColor := IfThen(iclr = SYS_DEFAULT_FOREGROUND_COLOR,
fmt.Background.FgColor := scBlack; scBlack, SetAsPaletteIndex(iclr));
// Background color // Background color
fmt.Background.BgColor := (rec.BkGr3 and $3F80) shr 7; iclr := (rec.BkGr3 and $3F80) shr 7;
if fmt.Background.BgColor = SYS_DEFAULT_BACKGROUND_COLOR then fmt.Background.BgColor := IfThen(iclr = SYS_DEFAULT_BACKGROUND_COLOR,
fmt.Background.BgColor := scTransparent; scTransparent, SetAsPaletteIndex(iclr));
// Fill style // Fill style
fmt.Background.Style := fs; fmt.Background.Style := fs;
Include(fmt.UsedFormattingFields, uffBackground); Include(fmt.UsedFormattingFields, uffBackground);
@ -1374,7 +1398,7 @@ begin
font := TsFont.Create; font := TsFont.Create;
{ Height of the font in twips = 1/20 of a point } { Height of the font in twips = 1/20 of a point }
lHeight := WordLEToN(AStream.ReadWord); // WordToLE(200) lHeight := WordLEToN(AStream.ReadWord);
font.Size := lHeight/20; font.Size := lHeight/20;
{ Option flags } { Option flags }
@ -1385,10 +1409,21 @@ begin
if lOptions and $0004 <> 0 then Include(font.Style, fssUnderline); if lOptions and $0004 <> 0 then Include(font.Style, fssUnderline);
if lOptions and $0008 <> 0 then Include(font.Style, fssStrikeout); if lOptions and $0008 <> 0 then Include(font.Style, fssStrikeout);
{ Colour index } { Color index }
// The problem is that the palette is loaded after the font list; therefore
// we do not know the rgb color of the font here. We store the palette index
// ("SetAsPaletteIndex") and replace it by the rgb color at the end of the
// workbook globals records. As an indicator that the font does not yet
// contain an rgb color a control bit is set in the high-byte of the TsColor.
lColor := WordLEToN(AStream.ReadWord); lColor := WordLEToN(AStream.ReadWord);
//font.Color := TsColor(lColor - 8); // Palette colors have an offset 8 if lColor < 8 then
font.Color := tsColor(lColor); // Use built-in colors directly otherwise the Workbook's FindFont would not find the font in ReadXF
font.Color := FPalette[lColor]
else
if lColor = SYS_DEFAULT_WINDOW_TEXT_COLOR then
font.Color := scBlack
else
font.Color := SetAsPaletteIndex(lColor);
{ Font weight } { Font weight }
lWeight := WordLEToN(AStream.ReadWord); lWeight := WordLEToN(AStream.ReadWord);
@ -1983,8 +2018,8 @@ begin
if fssStrikeout in AFont.Style then optn := optn or $0008; if fssStrikeout in AFont.Style then optn := optn or $0008;
AStream.WriteWord(WordToLE(optn)); AStream.WriteWord(WordToLE(optn));
{ Colour index } { Color index }
AStream.WriteWord(WordToLE(ord(FixColor(AFont.Color)))); AStream.WriteWord(WordToLE(PaletteIndex(AFont.Color)));
{ Font weight } { Font weight }
if fssBold in AFont.Style then if fssBold in AFont.Style then
@ -3005,8 +3040,8 @@ begin
if (AFormatRecord <> nil) and (uffBorder in AFormatRecord^.UsedFormattingFields) then if (AFormatRecord <> nil) and (uffBorder in AFormatRecord^.UsedFormattingFields) then
begin begin
// Left and right line colors // Left and right line colors
dw1 := AFormatRecord^.BorderStyles[cbWest].Color shl 16 + dw1 := PaletteIndex(AFormatRecord^.BorderStyles[cbWest].Color) shl 16 +
AFormatRecord^.BorderStyles[cbEast].Color shl 23; PaletteIndex(AFormatRecord^.BorderStyles[cbEast].Color) shl 23;
// Border line styles // Border line styles
if cbWest in AFormatRecord^.Border then if cbWest in AFormatRecord^.Border then
dw1 := dw1 or (DWord(AFormatRecord^.BorderStyles[cbWest].LineStyle)+1); dw1 := dw1 or (DWord(AFormatRecord^.BorderStyles[cbWest].LineStyle)+1);
@ -3022,9 +3057,9 @@ begin
dw1 := dw1 or $80000000; dw1 := dw1 or $80000000;
// Top, bottom and diagonal line colors // Top, bottom and diagonal line colors
dw2 := FixColor(AFormatRecord^.BorderStyles[cbNorth].Color) + dw2 := PaletteIndex(AFormatRecord^.BorderStyles[cbNorth].Color) +
FixColor(AFormatRecord^.BorderStyles[cbSouth].Color) shl 7 + PaletteIndex(AFormatRecord^.BorderStyles[cbSouth].Color) shl 7 +
FixColor(AFormatRecord^.BorderStyles[cbDiagUp].Color) shl 14; PaletteIndex(AFormatRecord^.BorderStyles[cbDiagUp].Color) shl 14;
// In BIFF8 both diagonals have the same color - we use the color of the up-diagonal. // In BIFF8 both diagonals have the same color - we use the color of the up-diagonal.
// Diagonal line style // Diagonal line style
@ -3041,11 +3076,11 @@ begin
// Pattern color // Pattern color
if AFormatRecord^.Background.FgColor = scTransparent if AFormatRecord^.Background.FgColor = scTransparent
then w3 := w3 or SYS_DEFAULT_FOREGROUND_COLOR then w3 := w3 or SYS_DEFAULT_FOREGROUND_COLOR
else w3 := w3 or FixColor(AFormatRecord^.Background.FgColor); else w3 := w3 or PaletteIndex(AFormatRecord^.Background.FgColor);
// Background color // Background color
if AFormatRecord^.Background.BgColor = scTransparent if AFormatRecord^.Background.BgColor = scTransparent
then w3 := w3 or SYS_DEFAULT_BACKGROUND_COLOR shl 7 then w3 := w3 or SYS_DEFAULT_BACKGROUND_COLOR shl 7
else w3 := w3 or (FixColor(AFormatRecord^.Background.BgColor) shl 7); else w3 := w3 or (PaletteIndex(AFormatRecord^.Background.BgColor) shl 7);
end; end;
rec.Border_BkGr1 := DWordToLE(dw1); rec.Border_BkGr1 := DWordToLE(dw1);
@ -3063,7 +3098,7 @@ initialization
RegisterSpreadFormat(TsSpreadBIFF8Reader, TsSpreadBIFF8Writer, sfExcel8); RegisterSpreadFormat(TsSpreadBIFF8Reader, TsSpreadBIFF8Writer, sfExcel8);
// Converts the palette to litte-endian // Converts the palette to litte-endian
MakeLEPalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8)); MakeLEPalette(PALETTE_BIFF8);
end. end.

View File

@ -11,7 +11,7 @@ interface
uses uses
Classes, SysUtils, DateUtils, lconvencoding, Classes, SysUtils, DateUtils, lconvencoding,
fpsTypes, fpSpreadsheet, fpsUtils, fpsNumFormatParser, fpsTypes, fpSpreadsheet, fpsUtils, fpsNumFormatParser, fpsPalette,
fpsReaderWriter; fpsReaderWriter;
const const
@ -211,7 +211,7 @@ const
{ System colors, for BIFF5-BIFF8 } { System colors, for BIFF5-BIFF8 }
SYS_DEFAULT_FOREGROUND_COLOR = $0040; SYS_DEFAULT_FOREGROUND_COLOR = $0040;
SYS_DEFAULT_BACKGROUND_COLOR = $0041; SYS_DEFAULT_BACKGROUND_COLOR = $0041;
SYS_DEFAULT_WINDOW_TEXT_COLOR = $7FFF;
{ Error codes } { Error codes }
ERR_INTERSECTION_EMPTY = $00; // #NULL! ERR_INTERSECTION_EMPTY = $00; // #NULL!
@ -348,11 +348,11 @@ type
RecordSize: Word; RecordSize: Word;
FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding FCodepage: string; // in a format prepared for lconvencoding.ConvertEncoding
FDateMode: TDateMode; FDateMode: TDateMode;
FPaletteFound: Boolean;
FIncompleteCell: PCell; FIncompleteCell: PCell;
FIncompleteNote: String; FIncompleteNote: String;
FIncompleteNoteLength: Word; FIncompleteNoteLength: Word;
FFirstNumFormatIndexInFile: Integer; FFirstNumFormatIndexInFile: Integer;
FPalette: TsPalette;
procedure AddBuiltinNumFormats; override; procedure AddBuiltinNumFormats; override;
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual; //overload; procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual; //overload;
// Extracts a number out of an RK value // Extracts a number out of an RK value
@ -360,9 +360,12 @@ type
// Returns the numberformat for a given XF record // Returns the numberformat for a given XF record
procedure ExtractNumberFormat(AXFIndex: WORD; procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual; out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual;
procedure FixColors;
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value // Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat; function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
ANumberFormatStr: String; out ADateTime: TDateTime): Boolean; ANumberFormatStr: String; out ADateTime: TDateTime): Boolean;
procedure PopulatePalette; virtual;
// Here we can add reading of records which didn't change across BIFF5-8 versions // Here we can add reading of records which didn't change across BIFF5-8 versions
// Read a blank cell // Read a blank cell
procedure ReadBlank(AStream: TStream); override; procedure ReadBlank(AStream: TStream); override;
@ -433,6 +436,7 @@ type
public public
constructor Create(AWorkbook: TsWorkbook); override; constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
end; end;
@ -443,12 +447,13 @@ type
FDateMode: TDateMode; FDateMode: TDateMode;
FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding
FFirstNumFormatIndexInFile: Integer; FFirstNumFormatIndexInFile: Integer;
FPalette: TsPalette;
procedure AddBuiltinNumFormats; override; procedure AddBuiltinNumFormats; override;
function FindXFIndex(ACell: PCell): Integer; virtual; function FindXFIndex(ACell: PCell): Integer; virtual;
function FixColor(AColor: TsColor): TsColor; override;
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer; function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
function GetLastColIndex(AWorksheet: TsWorksheet): Word; function GetLastColIndex(AWorksheet: TsWorksheet): Word;
function GetPrintOptions: Word; virtual; function GetPrintOptions: Word; virtual;
function PaletteIndex(AColor: TsColor): Word;
// Helper function for writing the BIFF header // Helper function for writing the BIFF header
procedure WriteBIFFHeader(AStream: TStream; ARecID, ARecSize: Word); procedure WriteBIFFHeader(AStream: TStream; ARecID, ARecSize: Word);
@ -548,6 +553,8 @@ type
public public
constructor Create(AWorkbook: TsWorkbook); override; constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
procedure CheckLimitations; override;
end; end;
procedure AddBuiltinBiffFormats(AList: TStringList; procedure AddBuiltinBiffFormats(AList: TStringList;
@ -773,16 +780,31 @@ end;
constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook); constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FPalette := TsPalette.Create;
PopulatePalette;
FCellFormatList := TsCellFormatList.Create(true); FCellFormatList := TsCellFormatList.Create(true);
// Allow duplicates! XF indexes get out of sync if not all format records are in list // 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 // Initial base date in case it won't be read from file
FDateMode := dm1900; FDateMode := dm1900;
// Limitations of BIFF5 and BIFF8 file format // Limitations of BIFF5 and BIFF8 file format
FLimitations.MaxColCount := 256; FLimitations.MaxColCount := 256;
FLimitations.MaxRowCount := 65536; FLimitations.MaxRowCount := 65536;
FLimitations.MaxPaletteSize := 64; FLimitations.MaxPaletteSize := 64;
end; end;
{@@ ----------------------------------------------------------------------------
Destructor of the reader class
-------------------------------------------------------------------------------}
destructor TsSpreadBIFFReader.Destroy;
begin
FPalette.Free;
inherited Destroy;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Adds the built-in number formats to the NumFormatList. Adds the built-in number formats to the NumFormatList.
Valid for BIFF5...BIFF8. Needs to be overridden for BIFF2. Valid for BIFF5...BIFF8. Needs to be overridden for BIFF2.
@ -814,7 +836,6 @@ begin
end; end;
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Extracts a number out of an RK value. Extracts a number out of an RK value.
Valid since BIFF3. Valid since BIFF3.
@ -871,6 +892,47 @@ begin
end; end;
end; end;
{@@ ----------------------------------------------------------------------------
It is a problem of the biff file structure that the font is loaded before the
palette. Therefore, when reading the font, we cannot determine its rgb color.
We had stored temporarily the palette index in the font color member and
are replacing it here by the corresponding rgb color. This is possible because
FixFontColors is called at the end of the workbook globals records when
everything is known.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.FixColors;
var
i: Integer;
fnt: TsFont;
fmt: PsCellFormat;
procedure FixColor(var AColor: TsColor);
begin
if IsPaletteIndex(AColor) then
AColor := FPalette[AColor and $00FFFFFF];
end;
begin
for i:=0 to FWorkbook.GetFontCount - 1 do
begin
fnt := FWorkbook.GetFont(i);
FixColor(fnt.Color);
end;
for i:=0 to FCellFormatList.Count-1 do
begin
fmt := FCellFormatList[i];
FixColor(fmt^.Background.BgColor);
FixColor(fmt^.Background.FgColor);
FixColor(fmt^.BorderStyles[cbEast].Color);
FixColor(fmt^.BorderStyles[cbWest].Color);
FixColor(fmt^.BorderStyles[cbNorth].Color);
FixColor(fmt^.BorderStyles[cbSouth].Color);
FixColor(fmt^.BorderStyles[cbDiagUp].Color);
FixColor(fmt^.BorderStyles[cbDiagDown].Color);
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Converts the number to a date/time and return that if it is Converts the number to a date/time and return that if it is
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -1463,17 +1525,15 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadPalette(AStream: TStream); procedure TsSpreadBIFFReader.ReadPalette(AStream: TStream);
var var
i, n: Word; n: Word;
pal: Array of TsColorValue;
begin begin
// Read palette size
n := WordLEToN(AStream.ReadWord) + 8; n := WordLEToN(AStream.ReadWord) + 8;
SetLength(pal, n); FPalette.Clear;
for i:=0 to 7 do FPalette.AddBuiltinColors;
pal[i] := Workbook.GetPaletteColor(i); // Read palette colors and add them to the palette
for i:=8 to n-1 do while FPalette.Count < n do
pal[i] := DWordLEToN(AStream.ReadDWord); FPalette.AddColor(DWordLEToN(AStream.ReadDWord));
Workbook.UsePalette(@pal[0], n, false);
FPaletteFound := true;
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -2129,6 +2189,15 @@ begin
FWorksheet.Options := FWorksheet.Options - [soHasFrozenPanes]; FWorksheet.Options := FWorksheet.Options - [soHasFrozenPanes];
end; end;
{@@ ----------------------------------------------------------------------------
Populates the reader's palette by default colors. Will be overwritten if the
file contains a palette on its own
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.PopulatePalette;
begin
FPalette.AddBuiltinColors;
end;
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
{ TsSpreadBIFFWriter } { TsSpreadBIFFWriter }
@ -2142,14 +2211,25 @@ constructor TsSpreadBIFFWriter.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
// Initial base date in case it won't be set otherwise.
// Use 1900 to get a bit more range between 1900..1904.
FDateMode := dm1900;
// Limitations of BIFF5 and BIFF8 file formats // Limitations of BIFF5 and BIFF8 file formats
FLimitations.MaxColCount := 256; FLimitations.MaxColCount := 256;
FLimitations.MaxRowCount := 65536; FLimitations.MaxRowCount := 65536;
FLimitations.MaxPaletteSize := 64; FLimitations.MaxPaletteSize := 64;
// Initial base date in case it won't be set otherwise.
// Use 1900 to get a bit more range between 1900..1904.
FDateMode := dm1900;
// Color palette
FPalette := TsPalette.Create;
FPalette.AddBuiltinColors;
FPalette.CollectFromWorkbook(AWorkbook);
end;
destructor TsSpreadBIFFWriter.Destroy;
begin
FPalette.Free;
inherited Destroy;
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -2164,6 +2244,21 @@ begin
); );
end; end;
{@@ ----------------------------------------------------------------------------
Checks limitations of the file format. Overridden to take care of the
color palette which can only contain a given number of entries.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.CheckLimitations;
begin
inherited CheckLimitations;
// Check color count.
if FPalette.Count > FLimitations.MaxPaletteSize then
begin
Workbook.AddErrorMsg(rsTooManyPaletteColors, [FPalette.Count, FLimitations.MaxPaletteSize]);
FPalette.Trim(FLimitations.MaxPaletteSize);
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Determines the index of the XF record, according to formatting of the Determines the index of the XF record, according to formatting of the
given cell given cell
@ -2173,17 +2268,6 @@ begin
Result := LAST_BUILTIN_XF + ACell^.FormatIndex; Result := LAST_BUILTIN_XF + ACell^.FormatIndex;
end; end;
function TsSpreadBIFFWriter.FixColor(AColor: TsColor): TsColor;
var
rgb: TsColorValue;
begin
if AColor >= Limitations.MaxPaletteSize then begin
rgb := Workbook.GetPaletteColor(AColor);
Result := Workbook.FindClosestColor(rgb, FLimitations.MaxPaletteSize);
end else
Result := AColor;
end;
function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer; function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
begin begin
Result := AWorksheet.GetLastRowIndex; Result := AWorksheet.GetLastRowIndex;
@ -2234,6 +2318,20 @@ begin
Result := Result or $0080; Result := Result or $0080;
end; end;
{@@ ----------------------------------------------------------------------------
Determines the index of the specified color in the writer's palette, or, if
not found, gets the index of the "closest" color.
-------------------------------------------------------------------------------}
function TsSpreadBIFFWriter.PaletteIndex(AColor: TsColor): Word;
var
idx: Integer;
begin
idx := FPalette.FindColor(AColor, Limitations.MaxPaletteSize);
if idx = -1 then
idx := FPalette.FindClosestColorIndex(AColor, Limitations.MaxPaletteSize);
Result := word(idx);
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes the BIFF record header consisting of the record ID and the size of Writes the BIFF record header consisting of the record ID and the size of
data to be written immediately afterwards. data to be written immediately afterwards.
@ -2695,14 +2793,14 @@ end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes the PALETTE record for the color palette. Writes the PALETTE record for the color palette.
Valid for BIFF3-BIFF8. BIFF2 has no palette in file. Valid for BIFF3-BIFF8. BIFF2 has no palette in the file.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WritePalette(AStream: TStream); procedure TsSpreadBIFFWriter.WritePalette(AStream: TStream);
const const
NUM_COLORS = 56; NUM_COLORS = 56;
var var
i, n: Integer; i, n: Integer;
rgb: TsColorValue; rgb: TsColor;
begin begin
{ BIFF Record header } { BIFF Record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_PALETTE, 2 + 4*NUM_COLORS); WriteBIFFHeader(AStream, INT_EXCEL_ID_PALETTE, 2 + 4*NUM_COLORS);
@ -2710,13 +2808,13 @@ begin
{ Number of colors } { Number of colors }
AStream.WriteWord(WordToLE(NUM_COLORS)); AStream.WriteWord(WordToLE(NUM_COLORS));
{ Take the colors from the palette of the Worksheet } { Take the colors from the internal palette of the writer }
n := Workbook.GetPaletteSize; n := FPalette.Count;
{ Skip the first 8 entries - they are hard-coded into Excel } { Skip the first 8 entries - they are hard-coded into Excel }
for i := 8 to 8 + NUM_COLORS - 1 do for i := 8 to 8 + NUM_COLORS - 1 do
begin begin
rgb := Math.IfThen(i < n, Workbook.GetPaletteColor(i), $FFFFFF); rgb := Math.IfThen(i < n, FPalette[i], $FFFFFF);
AStream.WriteDWord(DWordToLE(rgb)) AStream.WriteDWord(DWordToLE(rgb))
end; end;
end; end;

View File

@ -43,7 +43,7 @@ uses
{$ELSE} {$ELSE}
fpszipper, fpszipper,
{$ENDIF} {$ENDIF}
fpsTypes, fpSpreadsheet, fpsUtils, fpsReaderWriter, fpsNumFormat, fpsTypes, fpSpreadsheet, fpsUtils, fpsReaderWriter, fpsNumFormat, fpsPalette,
fpsxmlcommon, xlsCommon; fpsxmlcommon, xlsCommon;
type type
@ -59,7 +59,8 @@ type
FBorderList: TFPList; FBorderList: TFPList;
FHyperlinkList: TFPList; FHyperlinkList: TFPList;
FSharedFormulaBaseList: TFPList; FSharedFormulaBaseList: TFPList;
FThemeColors: array of TsColorValue; FPalette: TsPalette;
FThemeColors: array of TsColor;
FWrittenByFPS: Boolean; FWrittenByFPS: Boolean;
procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer); procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
procedure ApplyHyperlinks(AWorksheet: TsWorksheet); procedure ApplyHyperlinks(AWorksheet: TsWorksheet);
@ -255,81 +256,7 @@ const
MIME_COMMENTS = MIME_SPREADML + '.comments+xml'; MIME_COMMENTS = MIME_SPREADML + '.comments+xml';
MIME_VMLDRAWING = MIME_OFFICEDOCUMENT + '.vmlDrawing'; MIME_VMLDRAWING = MIME_OFFICEDOCUMENT + '.vmlDrawing';
LAST_PALETTE_COLOR = $3F; // 63 LAST_PALETTE_INDEX = 63;
var
// the palette of the 64 default colors as "big-endian color" values
// (identical to BIFF8)
PALETTE_OOXML: array[$00..LAST_PALETTE_COLOR] of TsColorValue = (
$000000, // $00: black // 8 built-in default colors
$FFFFFF, // $01: white
$FF0000, // $02: red
$00FF00, // $03: green
$0000FF, // $04: blue
$FFFF00, // $05: yellow
$FF00FF, // $06: magenta
$00FFFF, // $07: cyan
$000000, // $08: EGA black
$FFFFFF, // $09: EGA white
$FF0000, // $0A: EGA red
$00FF00, // $0B: EGA green
$0000FF, // $0C: EGA blue
$FFFF00, // $0D: EGA yellow
$FF00FF, // $0E: EGA magenta
$00FFFF, // $0F: EGA cyan
$800000, // $10: EGA dark red
$008000, // $11: EGA dark green
$000080, // $12: EGA dark blue
$808000, // $13: EGA olive
$800080, // $14: EGA purple
$008080, // $15: EGA teal
$C0C0C0, // $16: EGA silver
$808080, // $17: EGA gray
$9999FF, // $18:
$993366, // $19:
$FFFFCC, // $1A:
$CCFFFF, // $1B:
$660066, // $1C:
$FF8080, // $1D:
$0066CC, // $1E:
$CCCCFF, // $1F:
$000080, // $20:
$FF00FF, // $21:
$FFFF00, // $22:
$00FFFF, // $23:
$800080, // $24:
$800000, // $25:
$008080, // $26:
$0000FF, // $27:
$00CCFF, // $28:
$CCFFFF, // $29:
$CCFFCC, // $2A:
$FFFF99, // $2B:
$99CCFF, // $2C:
$FF99CC, // $2D:
$CC99FF, // $2E:
$FFCC99, // $2F:
$3366FF, // $30:
$33CCCC, // $31:
$99CC00, // $32:
$FFCC00, // $33:
$FF9900, // $34:
$FF6600, // $35:
$666699, // $36:
$969696, // $37:
$003366, // $38:
$339966, // $39:
$003300, // $3A:
$333300, // $3B:
$993300, // $3C:
$993366, // $3D:
$333399, // $3E:
$333333 // $3F:
);
type type
TFillListData = class TFillListData = class
@ -384,8 +311,6 @@ constructor TsSpreadOOXMLReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FDateMode := XlsxSettings.DateMode; FDateMode := XlsxSettings.DateMode;
// Set up the default palette in order to have the default color names correct.
Workbook.UseDefaultPalette;
FSharedStrings := TStringList.Create; FSharedStrings := TStringList.Create;
FFillList := TFPList.Create; FFillList := TFPList.Create;
@ -395,6 +320,8 @@ begin
// Allow duplicates because xf indexes used in cell records cannot be found any more. // Allow duplicates because xf indexes used in cell records cannot be found any more.
FSharedFormulaBaseList := TFPList.Create; FSharedFormulaBaseList := TFPList.Create;
FPalette := TsPalette.Create;
FPointSeparatorSettings := DefaultFormatSettings; FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.'; FPointSeparatorSettings.DecimalSeparator := '.';
end; end;
@ -417,6 +344,7 @@ begin
// FCellFormatList, FNumFormatList and FFontList are destroyed by ancestor // FCellFormatList, FNumFormatList and FFontList are destroyed by ancestor
FPalette.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -894,7 +822,7 @@ end;
function TsSpreadOOXMLReader.ReadColor(ANode: TDOMNode): TsColor; function TsSpreadOOXMLReader.ReadColor(ANode: TDOMNode): TsColor;
var var
s: String; s: String;
rgb: TsColorValue; rgb: TsColor;
idx: Integer; idx: Integer;
tint: Double; tint: Double;
n: Integer; n: Integer;
@ -912,16 +840,19 @@ begin
s := GetAttrValue(ANode, 'rgb'); s := GetAttrValue(ANode, 'rgb');
if s <> '' then begin if s <> '' then begin
Result := FWorkbook.AddColorToPalette(HTMLColorStrToColor('#' + s)); Result := HTMLColorStrToColor('#' + s);
exit; exit;
end; end;
s := GetAttrValue(ANode, 'indexed'); s := GetAttrValue(ANode, 'indexed');
if s <> '' then begin if s <> '' then begin
Result := StrToInt(s); Result := StrToInt(s);
n := FWorkbook.GetPaletteSize; n := FPalette.Count;
if (Result <= LAST_PALETTE_COLOR) and (Result < n) then if (Result <= LAST_PALETTE_INDEX) and (Result < n) then
begin
Result := FPalette[Result];
exit; exit;
end;
// System colors // System colors
// taken from OpenOffice docs // taken from OpenOffice docs
case Result of case Result of
@ -956,7 +887,7 @@ begin
tint := StrToFloat(s, FPointSeparatorSettings); tint := StrToFloat(s, FPointSeparatorSettings);
rgb := TintedColor(rgb, tint); rgb := TintedColor(rgb, tint);
end; end;
Result := FWorkBook.AddColorToPalette(rgb); Result := rgb;
exit; exit;
end; end;
end; end;
@ -1465,36 +1396,42 @@ var
node, colornode: TDOMNode; node, colornode: TDOMNode;
nodename: String; nodename: String;
s: string; s: string;
clr: TsColor; cidx: Integer; // color index
rgb: TsColorValue; rgb: TsColor;
n: Integer; n: Integer;
begin begin
// OOXML sometimes specifies color by index even if a palette ("indexedColors") // OOXML sometimes specifies color by index even if a palette ("indexedColors")
// is not loaeded. Therefore, we use the BIFF8 palette as default because // is not loaeded. Therefore, we use the BIFF8 palette as default because
// the default indexedColors are identical to it. // the default indexedColors are identical to it.
n := Length(PALETTE_OOXML); FPalette.Clear;
FWorkbook.UsePalette(@PALETTE_OOXML, n); FPalette.AddBuiltinColors; // This adds the BIFF2 colors 0..7
FPalette.AddExcelColors; // This adds the BIFF8 colors 8..63
n := FPalette.Count;
if ANode = nil then if ANode = nil then
exit; exit;
clr := 0; cidx := 0;
node := ANode.FirstChild; node := ANode.FirstChild;
while Assigned(node) do begin while Assigned(node) do
begin
nodename := node.NodeName; nodename := node.NodeName;
if nodename = 'indexedColors' then begin if nodename = 'indexedColors' then
begin
colornode := node.FirstChild; colornode := node.FirstChild;
while Assigned(colornode) do begin while Assigned(colornode) do
begin
nodename := colornode.NodeName; nodename := colornode.NodeName;
if nodename = 'rgbColor' then begin if nodename = 'rgbColor' then begin
s := GetAttrValue(colornode, 'rgb'); s := GetAttrValue(colornode, 'rgb');
if s <> '' then begin if s <> '' then begin
rgb := HTMLColorStrToColor('#' + s); rgb := HTMLColorStrToColor('#' + s);
if clr < n then begin if cidx < n then begin
FWorkbook.SetPaletteColor(clr, rgb); FPalette[cidx] := rgb;
inc(clr); inc(cidx);
end end
else else
FWorkbook.AddColorToPalette(rgb); FPalette.AddColor(rgb);
end; end;
end; end;
colornode := colorNode.NextSibling; colornode := colorNode.NextSibling;
@ -2078,20 +2015,19 @@ const
"slantDashDot", "mediumDashDot", "mediumDashed", "medium", "thick", "double" } "slantDashDot", "mediumDashDot", "mediumDashed", "medium", "thick", "double" }
var var
styleName: String; styleName: String;
colorName: String; colorStr: String;
rgb: TsColorValue; rgb: TsColor;
begin begin
if (ABorder in AFormatRecord^.Border) then begin if (ABorder in AFormatRecord^.Border) then begin
// Line style // Line style
styleName := LINESTYLE_NAME[AFormatRecord^.BorderStyles[ABorder].LineStyle]; styleName := LINESTYLE_NAME[AFormatRecord^.BorderStyles[ABorder].LineStyle];
// Border color // Border color
rgb := Workbook.GetPaletteColor(AFormatRecord^.BorderStyles[ABorder].Color); rgb := AFormatRecord^.BorderStyles[ABorder].Color;
//rgb := Workbook.GetPaletteColor(ACell^.BorderStyles[ABorder].Color); colorStr := ColorToHTMLColorStr(rgb, true);
colorName := ColorToHTMLColorStr(rgb, true);
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<%s style="%s"><color rgb="%s" /></%s>', '<%s style="%s"><color rgb="%s" /></%s>',
[ABorderName, styleName, colorName, ABorderName] [ABorderName, styleName, colorStr, ABorderName]
)); ));
end else end else
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
@ -2255,11 +2191,11 @@ begin
if FFillList[i]^.Background.FgColor = scTransparent then if FFillList[i]^.Background.FgColor = scTransparent then
fc := 'auto="1"' fc := 'auto="1"'
else else
fc := Format('rgb="%s"', [Copy(Workbook.GetPaletteColorAsHTMLStr(FFillList[i]^.Background.FgColor), 2, 255)]); fc := Format('rgb="%s"', [Copy(ColorToHTMLColorStr(FFillList[i]^.Background.FgColor), 2, MaxInt)]);
if FFillList[i]^.Background.BgColor = scTransparent then if FFillList[i]^.Background.BgColor = scTransparent then
bc := 'auto="1"' bc := 'auto="1"'
else else
bc := Format('rgb="%s"', [Copy(Workbook.GetPaletteColorAsHTMLStr(FFillList[i]^.Background.BgColor), 2, 255)]); bc := Format('rgb="%s"', [Copy(ColorToHTMLColorStr(FFillList[i]^.Background.BgColor), 2, MaxInt)]);
AppendToStream(AStream, AppendToStream(AStream,
'<fill>'); '<fill>');
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
@ -2283,19 +2219,11 @@ var
i: Integer; i: Integer;
font: TsFont; font: TsFont;
s: String; s: String;
rgb: TsColorValue;
begin begin
AppendToStream(FSStyles, Format( AppendToStream(FSStyles, Format(
'<fonts count="%d">', [Workbook.GetFontCount])); '<fonts count="%d">', [Workbook.GetFontCount]));
for i:=0 to Workbook.GetFontCount-1 do begin for i:=0 to Workbook.GetFontCount-1 do begin
font := Workbook.GetFont(i); font := Workbook.GetFont(i);
{
if font = 4 then
// if font = nil then
AppendToStream(AStream, '<font />')
// Font #4 is missing in fpspreadsheet due to BIFF compatibility. We write
// an empty node to keep the numbers in sync with the stored font index.
else begin}
s := Format('<sz val="%g" /><name val="%s" />', [font.Size, font.FontName], FPointSeparatorSettings); s := Format('<sz val="%g" /><name val="%s" />', [font.Size, font.FontName], FPointSeparatorSettings);
if (fssBold in font.Style) then if (fssBold in font.Style) then
s := s + '<b />'; s := s + '<b />';
@ -2305,17 +2233,10 @@ begin
s := s + '<u />'; s := s + '<u />';
if (fssStrikeout in font.Style) then if (fssStrikeout in font.Style) then
s := s + '<strike />'; s := s + '<strike />';
if font.Color <> scBlack then begin if font.Color <> scBlack then
if font.Color < 64 then s := s + Format('<color rgb="%s" />', [Copy(ColorToHTMLColorStr(font.Color), 2, MaxInt)]);
s := s + Format('<color indexed="%d" />', [font.Color])
else begin
rgb := Workbook.GetPaletteColor(font.Color);
s := s + Format('<color rgb="%s" />', [Copy(ColorToHTMLColorStr(rgb), 2, 255)]);
end;
end;
AppendToStream(AStream, AppendToStream(AStream,
'<font>', s, '</font>'); '<font>', s, '</font>');
// end;
end; end;
AppendToStream(AStream, AppendToStream(AStream,
'</fonts>'); '</fonts>');
@ -2481,27 +2402,11 @@ begin
); );
end; end;
{ Writes the workbook's color palette to the file } { In older versions, the workbook had a color palette which was written here.
Now there is no palette any more. }
procedure TsSpreadOOXMLWriter.WritePalette(AStream: TStream); procedure TsSpreadOOXMLWriter.WritePalette(AStream: TStream);
var
rgb: TsColorValue;
i: Integer;
begin begin
AppendToStream(AStream, // just keep it here in case we'd need it later...
'<colors>' +
'<indexedColors>');
// There must not be more than 64 palette entries because the next colors
// are system colors.
for i:=0 to Min(LAST_PALETTE_COLOR, Workbook.GetPaletteSize-1) do begin
rgb := Workbook.GetPaletteColor(i);
AppendToStream(AStream,
'<rgbColor rgb="'+ColorToHTMLColorStr(rgb, true) + '" />');
end;
AppendToStream(AStream,
'</indexedColors>' +
'</colors>');
end; end;
procedure TsSpreadOOXMLWriter.WritePageMargins(AStream: TStream; procedure TsSpreadOOXMLWriter.WritePageMargins(AStream: TStream;
@ -3636,8 +3541,5 @@ initialization
// Registers this reader / writer on fpSpreadsheet // Registers this reader / writer on fpSpreadsheet
RegisterSpreadFormat(TsSpreadOOXMLReader, TsSpreadOOXMLWriter, sfOOXML); RegisterSpreadFormat(TsSpreadOOXMLReader, TsSpreadOOXMLWriter, sfOOXML);
// Create color palette for OOXML file format
MakeLEPalette(@PALETTE_OOXML, Length(PALETTE_OOXML));
end. end.