You've already forked lazarus-ccr
fpspreadsheet: Support reading of number formats in xlsx files.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3382 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -60,12 +60,19 @@ type
|
|||||||
private
|
private
|
||||||
FPointSeparatorSettings: TFormatSettings;
|
FPointSeparatorSettings: TFormatSettings;
|
||||||
FSharedStrings: TStringList;
|
FSharedStrings: TStringList;
|
||||||
|
FXfList: TFPList;
|
||||||
|
FFillList: TFPList;
|
||||||
|
FBorderList: TFPList;
|
||||||
|
procedure ReadCellXfs(ANode: TDOMNode);
|
||||||
procedure ReadFont(ANode: TDOMNode);
|
procedure ReadFont(ANode: TDOMNode);
|
||||||
procedure ReadFonts(ANode: TDOMNode);
|
procedure ReadFonts(ANode: TDOMNode);
|
||||||
|
procedure ReadNumFormats(ANode: TDOMNode);
|
||||||
procedure ReadSharedStrings(ANode: TDOMNode);
|
procedure ReadSharedStrings(ANode: TDOMNode);
|
||||||
procedure ReadSheetList(ANode: TDOMNode; AList: TStrings);
|
procedure ReadSheetList(ANode: TDOMNode; AList: TStrings);
|
||||||
procedure ReadWorksheet(ANode: TDOMNode; ASheet: TsWorksheet);
|
procedure ReadWorksheet(ANode: TDOMNode; ASheet: TsWorksheet);
|
||||||
protected
|
protected
|
||||||
|
procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
|
||||||
|
procedure CreateNumFormatList; override;
|
||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook); override;
|
constructor Create(AWorkbook: TsWorkbook); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -175,6 +182,14 @@ const
|
|||||||
MIME_STYLES = MIME_SPREADML + '.styles+xml';
|
MIME_STYLES = MIME_SPREADML + '.styles+xml';
|
||||||
MIME_STRINGS = MIME_SPREADML + '.sharedStrings+xml';
|
MIME_STRINGS = MIME_SPREADML + '.sharedStrings+xml';
|
||||||
|
|
||||||
|
type
|
||||||
|
TXFListData = class
|
||||||
|
NumFmtIndex: Integer;
|
||||||
|
FontIndex: Integer;
|
||||||
|
FillIndex: Integer;
|
||||||
|
BorderIndex: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TsOOXMLNumFormatList }
|
{ TsOOXMLNumFormatList }
|
||||||
|
|
||||||
@ -256,19 +271,140 @@ end;
|
|||||||
constructor TsSpreadOOXMLReader.Create(AWorkbook: TsWorkbook);
|
constructor TsSpreadOOXMLReader.Create(AWorkbook: TsWorkbook);
|
||||||
begin
|
begin
|
||||||
inherited Create(AWorkbook);
|
inherited Create(AWorkbook);
|
||||||
FSharedStrings := TStringList.Create;
|
|
||||||
FPointSeparatorSettings := DefaultFormatSettings;
|
|
||||||
FPointSeparatorSettings.DecimalSeparator := '.';
|
|
||||||
// Set up the default palette in order to have the default color names correct.
|
// Set up the default palette in order to have the default color names correct.
|
||||||
Workbook.UseDefaultPalette;
|
Workbook.UseDefaultPalette;
|
||||||
|
|
||||||
|
FSharedStrings := TStringList.Create;
|
||||||
|
FFillList := TFPList.Create;
|
||||||
|
FBorderList := TFPList.Create;
|
||||||
|
FXfList := TFPList.Create;
|
||||||
|
|
||||||
|
FPointSeparatorSettings := DefaultFormatSettings;
|
||||||
|
FPointSeparatorSettings.DecimalSeparator := '.';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TsSpreadOOXMLReader.Destroy;
|
destructor TsSpreadOOXMLReader.Destroy;
|
||||||
|
var
|
||||||
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
|
for j := FXfList.Count-1 downto 0 do TObject(FXfList[j]).Free;
|
||||||
|
FXfList.Free;
|
||||||
|
|
||||||
|
for j := FFillList.Count-1 downto 0 do TObject(FFillList[j]).Free;
|
||||||
|
FFillList.Free;
|
||||||
|
|
||||||
|
for j := FBorderList.Count-1 downto 0 do TObject(FBorderList[j]).Free;
|
||||||
|
FBorderList.Free;
|
||||||
|
|
||||||
FSharedStrings.Free;
|
FSharedStrings.Free;
|
||||||
|
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadOOXMLReader.ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
|
||||||
|
var
|
||||||
|
xf: TXfListData;
|
||||||
|
numFmtData: TsNumFormatData;
|
||||||
|
j: Integer;
|
||||||
|
begin
|
||||||
|
if Assigned(ACell) then begin
|
||||||
|
xf := TXFListData(FXfList.Items[XfIndex]);
|
||||||
|
|
||||||
|
// Font
|
||||||
|
if xf.FontIndex = 1 then
|
||||||
|
Include(ACell^.UsedFormattingFields, uffBold)
|
||||||
|
else
|
||||||
|
if xf.FontIndex > 1 then
|
||||||
|
Include(ACell^.UsedFormattingFields, uffFont);
|
||||||
|
ACell^.FontIndex := xf.FontIndex;
|
||||||
|
(*
|
||||||
|
// Alignment
|
||||||
|
ACell^.HorAlignment := xf.HorAlignment;
|
||||||
|
ACell^.VertAlignment := xf.VertAlignment;
|
||||||
|
|
||||||
|
// Word wrap
|
||||||
|
if xf.WordWrap then
|
||||||
|
Include(ACell^.UsedFormattingFields, uffWordWrap)
|
||||||
|
else
|
||||||
|
Exclude(ACell^.UsedFormattingFields, uffWordWrap);
|
||||||
|
|
||||||
|
// Text rotation
|
||||||
|
if xf.TextRotation > trHorizontal then
|
||||||
|
Include(ACell^.UsedFormattingFields, uffTextRotation)
|
||||||
|
else
|
||||||
|
Exclude(ACell^.UsedFormattingFields, uffTextRotation);
|
||||||
|
ACell^.TextRotation := xf.TextRotation;
|
||||||
|
|
||||||
|
// Borders
|
||||||
|
ACell^.BorderStyles := xf.BorderStyles;
|
||||||
|
if xf.Borders <> [] then begin
|
||||||
|
Include(ACell^.UsedFormattingFields, uffBorder);
|
||||||
|
ACell^.Border := xf.Borders;
|
||||||
|
end else
|
||||||
|
Exclude(ACell^.UsedFormattingFields, uffBorder);
|
||||||
|
|
||||||
|
// Background color
|
||||||
|
if xf.BackgroundColor <> scTransparent then begin
|
||||||
|
Include(ACell^.UsedFormattingFields, uffBackgroundColor);
|
||||||
|
ACell^.BackgroundColor := xf.BackgroundColor;
|
||||||
|
end else
|
||||||
|
Exclude(ACell^.UsedFormattingFields, uffBackgroundColor);
|
||||||
|
*)
|
||||||
|
|
||||||
|
if xf.NumFmtIndex > 0 then begin
|
||||||
|
j := NumFormatList.FindByIndex(xf.NumFmtIndex);
|
||||||
|
if j > -1then begin
|
||||||
|
numFmtData := NumFormatList[j];
|
||||||
|
Include(ACell^.UsedFormattingFields, uffNumberFormat);
|
||||||
|
ACell^.NumberFormat := numFmtData.NumFormat;
|
||||||
|
ACell^.NumberFormatStr := numFmtData.FormatString;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadOOXMLReader.CreateNumFormatList;
|
||||||
|
begin
|
||||||
|
FreeAndNil(FNumFormatList);
|
||||||
|
FNumFormatList := TsOOXMLNumFormatList.Create(Workbook);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadOOXMLReader.ReadCellXfs(ANode: TDOMNode);
|
||||||
|
var
|
||||||
|
node: TDOMNode;
|
||||||
|
nodeName: String;
|
||||||
|
xf: TXfListData;
|
||||||
|
s1, s2: String;
|
||||||
|
begin
|
||||||
|
node := ANode.FirstChild;
|
||||||
|
while Assigned(node) do begin
|
||||||
|
nodeName := node.NodeName;
|
||||||
|
if nodeName = 'xf' then begin
|
||||||
|
xf := TXfListData.Create;
|
||||||
|
|
||||||
|
s1 := GetAttrValue(node, 'numFmtId');
|
||||||
|
s2 := GetAttrValue(node, 'applyNumberFormat');
|
||||||
|
if s2 = '1' then xf.NumFmtIndex := StrToInt(s1);
|
||||||
|
|
||||||
|
s1 := GetAttrValue(node, 'fontId');
|
||||||
|
s2 := GetAttrValue(node, 'applyFont');
|
||||||
|
if s2 = '1' then xf.FontIndex := StrToInt(s1);
|
||||||
|
|
||||||
|
s1 := GetAttrValue(node, 'fillId');
|
||||||
|
s2 := GetAttrValue(node, 'applyFill');
|
||||||
|
if s2 = '1' then xf.FillIndex := StrToInt(s1);
|
||||||
|
|
||||||
|
s1 := GetAttrValue(node, 'borderId');
|
||||||
|
s2 := GetAttrValue(node, 'applyBorder');
|
||||||
|
if s2 = '1' then xf.BorderIndex := StrToInt(s1);
|
||||||
|
|
||||||
|
FXfList.Add(xf);
|
||||||
|
end;
|
||||||
|
node := node.NextSibling;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOOXMLReader.ReadFont(ANode: TDOMNode);
|
procedure TsSpreadOOXMLReader.ReadFont(ANode: TDOMNode);
|
||||||
var
|
var
|
||||||
node: TDOMNode;
|
node: TDOMNode;
|
||||||
@ -343,6 +479,25 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadOOXMLReader.ReadNumFormats(ANode: TDOMNode);
|
||||||
|
var
|
||||||
|
node: TDOMNode;
|
||||||
|
idStr: String;
|
||||||
|
fmtStr: String;
|
||||||
|
nodeName: String;
|
||||||
|
begin
|
||||||
|
node := ANode.FirstChild;
|
||||||
|
while Assigned(node) do begin
|
||||||
|
nodeName := node.NodeName;
|
||||||
|
if nodeName = 'numFmt' then begin
|
||||||
|
idStr := GetAttrValue(node, 'numFmtId');
|
||||||
|
fmtStr := GetAttrValue(node, 'formatCode');
|
||||||
|
NumFormatList.AnalyzeAndAdd(StrToInt(idStr), fmtStr);
|
||||||
|
end;
|
||||||
|
node := node.NextSibling;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOOXMLReader.ReadSharedStrings(ANode: TDOMNode);
|
procedure TsSpreadOOXMLReader.ReadSharedStrings(ANode: TDOMNode);
|
||||||
var
|
var
|
||||||
valuenode: TDOMNode;
|
valuenode: TDOMNode;
|
||||||
@ -384,6 +539,7 @@ var
|
|||||||
formulaStr: String;
|
formulaStr: String;
|
||||||
sstIndex: Integer;
|
sstIndex: Integer;
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
|
number: Double;
|
||||||
begin
|
begin
|
||||||
rownode := ANode.FirstChild;
|
rownode := ANode.FirstChild;
|
||||||
while Assigned(rownode) do begin
|
while Assigned(rownode) do begin
|
||||||
@ -395,10 +551,6 @@ begin
|
|||||||
s := GetAttrValue(cellnode, 'r'); // cell address, like 'A1'
|
s := GetAttrValue(cellnode, 'r'); // cell address, like 'A1'
|
||||||
ParseCellString(s, rowIndex, colIndex);
|
ParseCellString(s, rowIndex, colIndex);
|
||||||
|
|
||||||
// get style index
|
|
||||||
s := GetAttrValue(cellnode, 's');
|
|
||||||
//..
|
|
||||||
|
|
||||||
// create cell
|
// create cell
|
||||||
if FIsVirtualMode then begin
|
if FIsVirtualMode then begin
|
||||||
InitCell(rowIndex, colIndex, FVirtualCell);
|
InitCell(rowIndex, colIndex, FVirtualCell);
|
||||||
@ -406,6 +558,10 @@ begin
|
|||||||
end else
|
end else
|
||||||
cell := ASheet.GetCell(rowIndex, colIndex);
|
cell := ASheet.GetCell(rowIndex, colIndex);
|
||||||
|
|
||||||
|
// get style index
|
||||||
|
s := GetAttrValue(cellnode, 's');
|
||||||
|
ApplyCellFormatting(cell, StrToInt(s));
|
||||||
|
|
||||||
// get data
|
// get data
|
||||||
datanode := cellnode.FirstChild;
|
datanode := cellnode.FirstChild;
|
||||||
dataStr := '';
|
dataStr := '';
|
||||||
@ -424,21 +580,34 @@ begin
|
|||||||
cell^.FormulaValue.FormulaStr := '=' + formulaStr;
|
cell^.FormulaValue.FormulaStr := '=' + formulaStr;
|
||||||
|
|
||||||
// get data type
|
// get data type
|
||||||
s := GetAttrValue(cellnode, 't'); // data type
|
s := GetAttrValue(cellnode, 't'); // "t" = data type
|
||||||
if s = 'n' then
|
if (s = '') and (dataStr = '') then
|
||||||
ASheet.WriteNumber(cell, StrToFloat(dataStr, FPointSeparatorSettings))
|
ASheet.WriteBlank(cell)
|
||||||
|
else
|
||||||
|
if (s = '') or (s = 'n') then begin
|
||||||
|
// Number or date/time, depending on format
|
||||||
|
number := StrToFloat(dataStr, FPointSeparatorSettings);
|
||||||
|
if IsDateTimeFormat(cell^.NumberFormatStr) then
|
||||||
|
ASheet.WriteDateTime(cell, number, cell^.NumberFormatStr)
|
||||||
|
else
|
||||||
|
ASheet.WriteNumber(cell, number);
|
||||||
|
end
|
||||||
else
|
else
|
||||||
if s = 's' then begin
|
if s = 's' then begin
|
||||||
|
// String from shared strings table
|
||||||
sstIndex := StrToInt(dataStr);
|
sstIndex := StrToInt(dataStr);
|
||||||
ASheet.WriteUTF8Text(cell, FSharedStrings[sstIndex]);
|
ASheet.WriteUTF8Text(cell, FSharedStrings[sstIndex]);
|
||||||
end else
|
end else
|
||||||
if s = 'str' then
|
if s = 'str' then
|
||||||
|
// literal string
|
||||||
ASheet.WriteUTF8Text(cell, datastr)
|
ASheet.WriteUTF8Text(cell, datastr)
|
||||||
else
|
else
|
||||||
if s = 'b' then
|
if s = 'b' then
|
||||||
|
// boolean
|
||||||
ASheet.WriteBoolValue(cell, dataStr='1')
|
ASheet.WriteBoolValue(cell, dataStr='1')
|
||||||
else
|
else
|
||||||
if s = 'e' then begin
|
if s = 'e' then begin
|
||||||
|
// error value
|
||||||
if dataStr = '#NULL!' then
|
if dataStr = '#NULL!' then
|
||||||
ASheet.WriteErrorValue(cell, errEmptyIntersection)
|
ASheet.WriteErrorValue(cell, errEmptyIntersection)
|
||||||
else if dataStr = '#DIV/0!' then
|
else if dataStr = '#DIV/0!' then
|
||||||
@ -456,12 +625,7 @@ begin
|
|||||||
else
|
else
|
||||||
raise Exception.Create('unknown error type');
|
raise Exception.Create('unknown error type');
|
||||||
end else
|
end else
|
||||||
if s = '' then begin
|
raise Exception.Create('Unknown data type');
|
||||||
if formulaStr = '' then
|
|
||||||
ASheet.WriteBlank(cell)
|
|
||||||
else
|
|
||||||
ASheet.WriteNumber(cell, STrToFloat(dataStr, FPointSeparatorSettings));
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
cellnode := cellnode.NextSibling;
|
cellnode := cellnode.NextSibling;
|
||||||
@ -518,14 +682,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// process the styles.xml file
|
// process the styles.xml file
|
||||||
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_STYLES);
|
if FileExists(FilePath + OOXML_PATH_XL_STYLES) then begin // should always exist, just to make sure...
|
||||||
DeleteFile(FilePath + OOXML_PATH_XL_STYLES);
|
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_STYLES);
|
||||||
ReadFonts(Doc.DocumentElement.FindNode('fonts'));
|
DeleteFile(FilePath + OOXML_PATH_XL_STYLES);
|
||||||
(*
|
ReadFonts(Doc.DocumentElement.FindNode('fonts'));
|
||||||
ReadNumFormats(StylesNode);
|
ReadNumFormats(Doc.DocumentElement.FindNode('numFmts'));
|
||||||
ReadStyles(StylesNode);
|
ReadCellXfs(Doc.DocumentElement.FindNode('cellXfs'));
|
||||||
*)
|
FreeAndNil(Doc);
|
||||||
FreeAndNil(Doc);
|
end;
|
||||||
|
|
||||||
// process the workbook.xml file
|
// process the workbook.xml file
|
||||||
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_WORKBOOK);
|
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_WORKBOOK);
|
||||||
|
Reference in New Issue
Block a user