FPSpreadsheet: Proper usage of Theme colors by xlsx chart reader.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9452 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2024-09-19 11:22:04 +00:00
parent 56886c8959
commit a8c183b57a
2 changed files with 81 additions and 40 deletions

View File

@ -87,8 +87,8 @@ type
FEmbeddedObjList: TFPList; FEmbeddedObjList: TFPList;
FHyperlinkList: TFPList; FHyperlinkList: TFPList;
FSharedFormulaBaseList: TFPList; FSharedFormulaBaseList: TFPList;
FThemeColorList: TFPList;
FPalette: TsPalette; FPalette: TsPalette;
FThemeColors: array of TsColor;
FLastRow, FLastCol: Cardinal; FLastRow, FLastCol: Cardinal;
FWrittenByFPS: Boolean; FWrittenByFPS: Boolean;
procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer); procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
@ -169,6 +169,7 @@ type
destructor Destroy; override; destructor Destroy; override;
class function CheckFileFormat(AStream: TStream): Boolean; override; class function CheckFileFormat(AStream: TStream): Boolean; override;
function CreateXMLStream: TStream; function CreateXMLStream: TStream;
function GetThemeColor(AName: String): Integer;
function NeedsPassword(AStream: TStream): Boolean; override; function NeedsPassword(AStream: TStream): Boolean; override;
procedure ReadFromStream(AStream: TStream; APassword: String = ''; procedure ReadFromStream(AStream: TStream; APassword: String = '';
AParams: TsStreamParams = []); override; AParams: TsStreamParams = []); override;
@ -488,6 +489,11 @@ type
RelId: String; RelId: String;
end; end;
TThemeColor = class
Name: String;
Color: TsColor;
end;
const const
PATTERN_TYPES: array [TsFillStyle] of string = ( PATTERN_TYPES: array [TsFillStyle] of string = (
'none', // fsNoFill 'none', // fsNoFill
@ -846,6 +852,7 @@ begin
FCellFormatList := TsCellFormatList.Create(true); FCellFormatList := TsCellFormatList.Create(true);
FDifferentialFormatList := TFPList.Create; FDifferentialFormatList := TFPList.Create;
FDrawingToSheetRelList := TFPList.Create; FDrawingToSheetRelList := TFPList.Create;
FThemeColorList := TFPList.Create;
FEmbeddedObjList := TFPList.Create; FEmbeddedObjList := TFPList.Create;
// 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;
@ -882,9 +889,14 @@ begin
TObject(FDifferentialFormatList[j]).Free; TObject(FDifferentialFormatList[j]).Free;
FDifferentialFormatList.Free; FDifferentialFormatList.Free;
for j := FThemeColorList.Count-1 downto 0 do
TObject(FThemeColorList[j]).Free;
FThemeColorList.Free;
for j := FDrawingToSheetRelList.Count-1 downto 0 do for j := FDrawingToSheetRelList.Count-1 downto 0 do
TObject(FDrawingToSheetRelList[j]).Free; TObject(FDrawingToSheetRelList[j]).Free;
FDrawingToSheetRelList.Free; FDrawingToSheetRelList.Free;
for j := FSheetList.Count-1 downto 0 do for j := FSheetList.Count-1 downto 0 do
TObject(FSheetList[j]).Free; TObject(FSheetList[j]).Free;
FSheetList.Free; FSheetList.Free;
@ -985,6 +997,21 @@ begin
Result := TMemoryStream.Create; Result := TMemoryStream.Create;
end; end;
function TsSpreadOOXMLReader.GetThemeColor(AName: String): Integer;
var
i: Integer;
begin
AName := 'a:' + AName;
for i := 0 to FThemeColorList.Count-1 do
if SameText(TThemeColor(FThemeColorList[i]).Name, AName) then
begin
Result := TThemeColor(FThemeColorList[i]).Color;
exit;
end;
Result := scNotDefined;
end;
{ Checks the file header for the signature of the decrypted file format. } { Checks the file header for the signature of the decrypted file format. }
class function TsSpreadOOXMLReader.IsEncrypted(AStream: TStream): Boolean; class function TsSpreadOOXMLReader.IsEncrypted(AStream: TStream): Boolean;
var var
@ -2108,7 +2135,7 @@ begin
s := GetAttrValue(ANode, 'theme'); s := GetAttrValue(ANode, 'theme');
if s <> '' then begin if s <> '' then begin
idx := StrToInt(s); idx := StrToInt(s);
if idx < Length(FThemeColors) then begin if idx < FThemeColorList.Count then begin
// For some reason the first two pairs of colors are interchanged in Excel! // For some reason the first two pairs of colors are interchanged in Excel!
case idx of case idx of
0: idx := 1; 0: idx := 1;
@ -2116,7 +2143,7 @@ begin
2: idx := 3; 2: idx := 3;
3: idx := 2; 3: idx := 2;
end; end;
rgb := FThemeColors[idx]; rgb := TThemeColor(FThemeColorList[idx]).Color;
s := GetAttrValue(ANode, 'tint'); s := GetAttrValue(ANode, 'tint');
if s <> '' then begin if s <> '' then begin
tint := StrToFloat(s, FPointSeparatorSettings); tint := StrToFloat(s, FPointSeparatorSettings);
@ -4349,14 +4376,37 @@ end;
procedure TsSpreadOOXMLReader.ReadThemeColors(ANode: TDOMNode); procedure TsSpreadOOXMLReader.ReadThemeColors(ANode: TDOMNode);
var var
child: TDOMNode;
clrNode: TDOMNode; clrNode: TDOMNode;
nodeName: String; nodeName: String;
j: Integer;
procedure AddColor(AColorStr: String); function GetColorFromNode(ANode: TDOMNode; AAttrName: String): String;
var
nodeName: String;
begin
while Assigned(ANode) do
begin
nodeName := ANode.NodeName;
if (nodeName = 'a:sysClr') or (nodeName = 'a:srgbClr') then
begin
Result := GetAttrValue(ANode, AAttrName);
exit;
end;
ANode := ANode.NextSibling;
end;
Result := '';
end;
procedure AddColor(AColorName, AColorStr: String);
var
themeClr: TThemeColor;
begin begin
if AColorStr <> '' then begin if AColorStr <> '' then begin
SetLength(FThemeColors, Length(FThemeColors)+1); themeClr := TThemeColor.Create;
FThemeColors[Length(FThemeColors)-1] := HTMLColorStrToColor('#' + AColorStr); themeClr.Name := AColorName;
themeClr.Color := HTMLColorStrToColor('#' + AColorStr);
FThemeColorList.Add(themeClr);
end; end;
end; end;
@ -4364,45 +4414,52 @@ begin
if not Assigned(ANode) then if not Assigned(ANode) then
exit; exit;
SetLength(FThemeColors, 0); if FThemeColorList.Count > 0 then
begin
for j := FThemeColorList.Count-1 downto 0 do
TObject(FThemeColorList[j]).Free;
FThemeColorList.Clear;
end;
clrNode := ANode.FirstChild; clrNode := ANode.FirstChild;
while Assigned(clrNode) do begin while Assigned(clrNode) do begin
nodeName := clrNode.NodeName; nodeName := clrNode.NodeName;
child := clrNode.FirstChild;
if nodeName = 'a:dk1' then if nodeName = 'a:dk1' then
AddColor(GetAttrValue(clrNode.FirstChild, 'lastClr')) AddColor(nodeName, GetColorFromNode(child, 'lastClr'))
else else
if nodeName = 'a:lt1' then if nodeName = 'a:lt1' then
AddColor(GetAttrValue(clrNode.FirstChild, 'lastClr')) AddColor(nodeName, GetColorFromNode(child, 'lastClr'))
else else
if nodeName = 'a:dk2' then if nodeName = 'a:dk2' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:lt2' then if nodeName = 'a:lt2' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:accent1' then if nodeName = 'a:accent1' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:accent2' then if nodeName = 'a:accent2' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:accent3' then if nodeName = 'a:accent3' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:accent4' then if nodeName = 'a:accent4' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:accent5' then if nodeName = 'a:accent5' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:accent6' then if nodeName = 'a:accent6' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:hlink' then if nodeName = 'a:hlink' then
AddColor(GetAttrValue(clrNode.FirstChild, 'val')) AddColor(nodeName, GetColorFromNode(child, 'val'))
else else
if nodeName = 'a:folHlink' then if nodeName = 'a:folHlink' then
AddColor(GetAttrValue(clrNode.FirstChild, 'aval')); AddColor(nodeName, GetColorFromNode(child, 'aval'));
clrNode := clrNode.NextSibling; clrNode := clrNode.NextSibling;
end; end;
end; end;

View File

@ -8,7 +8,7 @@ interface
{$ifdef FPS_CHARTS} {$ifdef FPS_CHARTS}
uses //LazLoggerBase, uses //LazLoggerBase,
Classes, SysUtils, StrUtils, Contnrs, FPImage, fgl, Classes, SysUtils, StrUtils, Contnrs, FPImage,
{$ifdef FPS_PATCHED_ZIPPER}fpszipper,{$else}zipper,{$endif} {$ifdef FPS_PATCHED_ZIPPER}fpszipper,{$else}zipper,{$endif}
laz2_xmlread, laz2_DOM, laz2_xmlread, laz2_DOM,
fpsTypes, fpSpreadsheet, fpsChart, fpsUtils, fpsNumFormat, fpsImages, fpsTypes, fpSpreadsheet, fpsChart, fpsUtils, fpsNumFormat, fpsImages,
@ -20,7 +20,6 @@ type
TsSpreadOOXMLChartReader = class(TsBasicSpreadChartReader) TsSpreadOOXMLChartReader = class(TsBasicSpreadChartReader)
private private
FPointSeparatorSettings: TFormatSettings; FPointSeparatorSettings: TFormatSettings;
FColors: specialize TFPGMap<string, TsColor>;
FImages: TFPObjectList; FImages: TFPObjectList;
FXAxisID, FYAxisID, FX2AxisID, FY2AxisID: DWord; FXAxisID, FYAxisID, FX2AxisID, FY2AxisID: DWord;
FXAxisDelete, FYAxisDelete, FX2AxisDelete, FY2AxisDelete: Boolean; FXAxisDelete, FYAxisDelete, FX2AxisDelete, FY2AxisDelete: Boolean;
@ -282,27 +281,12 @@ begin
FPointSeparatorSettings := SysUtils.DefaultFormatSettings; FPointSeparatorSettings := SysUtils.DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator:='.'; FPointSeparatorSettings.DecimalSeparator:='.';
// The following color values are directly copied from xlsx files written by Excel.
// In the long term, they should be read from xl/theme/theme1.xml.
FColors := specialize TFPGMap<string, TsColor>.Create;
FColors.Add('dk1', scBlack);
FColors.Add('lt1', scWhite);
FColors.Add('dk2', FlipColorBytes($44546A));
FColors.Add('lt2', FlipColorBytes($E7E6E6));
FColors.Add('accent1', FlipColorBytes($4472C4));
FColors.Add('accent2', FlipColorBytes($ED7D31));
FColors.Add('accent3', FlipColorBytes($A5A5A5));
FColors.Add('accent4', FlipColorBytes($FFC000));
FColors.Add('accent5', FlipColorBytes($5B9BD5));
FColors.Add('accent6', FlipColorBytes($70AD47));
FImages := TFPObjectList.Create; FImages := TFPObjectList.Create;
end; end;
destructor TsSpreadOOXMLChartReader.Destroy; destructor TsSpreadOOXMLChartReader.Destroy;
begin begin
FImages.Free; FImages.Free;
FColors.Free;
inherited; inherited;
end; end;
@ -672,9 +656,9 @@ procedure TsSpreadOOXMLChartReader.ReadChartColor(ANode: TDOMNode;
var var
nodeName, s: String; nodeName, s: String;
idx: Integer;
n: Integer; n: Integer;
child: TDOMNode; child: TDOMNode;
themeRGB: TsColor;
lumMod: Single = 1.0; lumMod: Single = 1.0;
lumOff: Single = 0.0; lumOff: Single = 0.0;
begin begin
@ -687,10 +671,10 @@ begin
s := GetAttrValue(ANode, 'val'); s := GetAttrValue(ANode, 'val');
if (s <> '') then if (s <> '') then
begin begin
idx := FColors.IndexOf(ColorAlias(s)); themeRGB := TsSpreadOOXMLReader(Reader).GetThemeColor(ColorAlias(s));
if idx > -1 then if themeRGB <> scNotDefined then
begin begin
AColor.Color := FColors.Data[idx]; AColor.Color := themeRGB;
child := ANode.FirstChild; child := ANode.FirstChild;
while Assigned(child) do while Assigned(child) do
begin begin