fpspreadsheet: Improved auto-format detection (check extension first, check file header when extension test fails). Fix ods reader crashing when extension has been renamed.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6760 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-12-18 00:07:16 +00:00
parent ca80c68d6b
commit 02281cd15f
3 changed files with 108 additions and 76 deletions

View File

@ -2613,97 +2613,103 @@ begin
XMLStream.Free; XMLStream.Free;
end; end;
ReadFontFaces(Doc.DocumentElement.FindNode('office:font-face-decls')); if Assigned(Doc) then begin
ReadFontFaces(Doc.DocumentElement.FindNode('office:font-face-decls'));
StylesNode := Doc.DocumentElement.FindNode('office:styles'); StylesNode := Doc.DocumentElement.FindNode('office:styles');
ReadNumFormats(StylesNode); ReadNumFormats(StylesNode);
ReadStyles(StylesNode); ReadStyles(StylesNode);
ReadAutomaticStyles(Doc.DocumentElement.FindNode('office:automatic-styles')); ReadAutomaticStyles(Doc.DocumentElement.FindNode('office:automatic-styles'));
ReadMasterStyles(Doc.DocumentElement.FindNode('office:master-styles')); ReadMasterStyles(Doc.DocumentElement.FindNode('office:master-styles'));
FreeAndNil(Doc); FreeAndNil(Doc);
end;
//process the content.xml file //process the content.xml file
XMLStream := CreateXMLStream; XMLStream := CreateXMLStream;
try try
if UnzipToStream(AStream, 'content.xml', XMLStream) then if UnzipToStream(AStream, 'content.xml', XMLStream) then
ReadXMLStream(Doc, XMLStream); ReadXMLStream(Doc, XMLStream)
else
raise EFPSpreadsheetReader.CreateFmt(rsDefectiveInternalFileStructure, ['ods']);
finally finally
XMLStream.Free; XMLStream.Free;
end; end;
ReadFontFaces(Doc.DocumentElement.FindNode('office:font-face-decls')); if Assigned(Doc) then begin
StylesNode := Doc.DocumentElement.FindNode('office:automatic-styles'); ReadFontFaces(Doc.DocumentElement.FindNode('office:font-face-decls'));
ReadNumFormats(StylesNode); StylesNode := Doc.DocumentElement.FindNode('office:automatic-styles');
ReadStyles(StylesNode); ReadNumFormats(StylesNode);
ReadStyles(StylesNode);
BodyNode := Doc.DocumentElement.FindNode('office:body'); BodyNode := Doc.DocumentElement.FindNode('office:body');
if not Assigned(BodyNode) then if not Assigned(BodyNode) then
raise EFPSpreadsheet.Create('[TsSpreadOpenDocReader.ReadFromStream] Node "office:body" not found.'); raise EFPSpreadsheet.Create('[TsSpreadOpenDocReader.ReadFromStream] Node "office:body" not found.');
SpreadSheetNode := BodyNode.FindNode('office:spreadsheet'); SpreadSheetNode := BodyNode.FindNode('office:spreadsheet');
if not Assigned(SpreadSheetNode) then if not Assigned(SpreadSheetNode) then
raise EFPSpreadsheet.Create('[TsSpreadOpenDocReader.ReadFromStream] Node "office:spreadsheet" not found.'); raise EFPSpreadsheet.Create('[TsSpreadOpenDocReader.ReadFromStream] Node "office:spreadsheet" not found.');
ReadSheets(SpreadsheetNode); ReadSheets(SpreadsheetNode);
ReadDocumentProtection(SpreadsheetNode); ReadDocumentProtection(SpreadsheetNode);
ReadDateMode(SpreadSheetNode); ReadDateMode(SpreadSheetNode);
//process each table (sheet) //process each table (sheet)
TableNode := SpreadSheetNode.FindNode('table:table'); TableNode := SpreadSheetNode.FindNode('table:table');
while Assigned(TableNode) do while Assigned(TableNode) do
begin
nodename := TableNode.Nodename;
// These nodes occur due to leading spaces which are not skipped
// automatically any more due to PreserveWhiteSpace option applied
// to ReadXMLFile
if nodeName <> 'table:table' then
begin begin
TableNode := TableNode.NextSibling; nodename := TableNode.Nodename;
continue; // These nodes occur due to leading spaces which are not skipped
end; // automatically any more due to PreserveWhiteSpace option applied
// to ReadXMLFile
if nodeName <> 'table:table' then
begin
TableNode := TableNode.NextSibling;
continue;
end;
// Tables with external references contain a copy of the external table // Tables with external references contain a copy of the external table
// having the filename as sheet name - which is not valid for fps. // having the filename as sheet name - which is not valid for fps.
// Since external references are not supported ATM we skip this table. // Since external references are not supported ATM we skip this table.
if TableNode.FindNode('table:table-source') <> nil then begin if TableNode.FindNode('table:table-source') <> nil then begin
TableNode := TableNode.NextSibling; TableNode := TableNode.NextSibling;
Continue; Continue;
end; end;
sheetName := GetAttrValue(TableNode, 'table:name'); sheetName := GetAttrValue(TableNode, 'table:name');
FWorksheet := TsWorkbook(FWorkbook).GetWorksheetByName(sheetName); FWorksheet := TsWorkbook(FWorkbook).GetWorksheetByName(sheetName);
// FWorkSheet := TsWorkbook(FWorkbook).AddWorksheet(sheetName, true); // FWorkSheet := TsWorkbook(FWorkbook).AddWorksheet(sheetName, true);
tablestyleName := GetAttrValue(TableNode, 'table:style-name'); tablestyleName := GetAttrValue(TableNode, 'table:style-name');
// Read protection // Read protection
ReadSheetProtection(TableNode, FWorksheet); ReadSheetProtection(TableNode, FWorksheet);
// Collect embedded images // Collect embedded images
ReadShapes(TableNode); ReadShapes(TableNode);
// Collect column styles used // Collect column styles used
ReadColumns(TableNode); ReadColumns(TableNode);
// Process each row inside the sheet and process each cell of the row // Process each row inside the sheet and process each cell of the row
ReadRowsAndCells(TableNode); ReadRowsAndCells(TableNode);
// Read page layout // Read page layout
ReadPageLayout(StylesNode, GetAttrValue(TableNode, 'table:style-name'), ReadPageLayout(StylesNode, GetAttrValue(TableNode, 'table:style-name'),
(FWorksheet as TsWorksheet).PageLayout); (FWorksheet as TsWorksheet).PageLayout);
// Repeated cols/rows already have been determined. // Repeated cols/rows already have been determined.
(FWorksheet as TsWorksheet).PageLayout.SetRepeatedRows( (FWorksheet as TsWorksheet).PageLayout.SetRepeatedRows(
FRepeatedRows.FirstIndex, FRepeatedRows.LastIndex); FRepeatedRows.FirstIndex, FRepeatedRows.LastIndex);
(FWorksheet as TsWorksheet).PageLayout.SetRepeatedCols( (FWorksheet as TsWorksheet).PageLayout.SetRepeatedCols(
FRepeatedCols.FirstIndex, FRepeatedCols.LastIndex); FRepeatedCols.FirstIndex, FRepeatedCols.LastIndex);
// Read print ranges // Read print ranges
ReadPrintRanges(TableNode, FWorksheet); ReadPrintRanges(TableNode, FWorksheet);
// Apply table style // Apply table style
ApplyTableStyle(FWorksheet, tablestylename); ApplyTableStyle(FWorksheet, tablestylename);
// Handle columns // Handle columns
ApplyColWidths; ApplyColWidths;
// Page layout // Page layout
FixCols(FWorksheet); FixCols(FWorksheet);
FixRows(FWorksheet); FixRows(FWorksheet);
// Continue with next table // Continue with next table
TableNode := TableNode.NextSibling; TableNode := TableNode.NextSibling;
end; //while Assigned(TableNode) end; //while Assigned(TableNode)
FreeAndNil(Doc); FreeAndNil(Doc);
end;
// process the settings.xml file (Note: it does not always exist!) // process the settings.xml file (Note: it does not always exist!)
XMLStream := CreateXMLStream; XMLStream := CreateXMLStream;
@ -2718,9 +2724,6 @@ begin
XMLStream.Free; XMLStream.Free;
end; end;
// Convert formulas from OpenDocument to ExcelA1 dialect
// FixFormulas;
// Active sheet // Active sheet
if FActiveSheet <> '' then if FActiveSheet <> '' then
sheet := (FWorkbook as TsWorkbook).GetWorksheetByName(FActiveSheet) else sheet := (FWorkbook as TsWorkbook).GetWorksheetByName(FActiveSheet) else

View File

@ -8691,18 +8691,47 @@ procedure TsWorkbook.ReadFromFile(AFileName: string; APassword: String = '';
AParams: TsStreamParams = []); AParams: TsStreamParams = []);
var var
success: Boolean; success: Boolean;
fmtID: TsSpreadFormatID;
fileFormats: TsSpreadFormatIDArray; fileFormats: TsSpreadFormatIDArray;
i: Integer; i: Integer;
found: Boolean;
begin begin
if not FileExists(AFileName) then if not FileExists(AFileName) then
raise EFPSpreadsheetReader.CreateFmt(rsFileNotFound, [AFileName]); raise EFPSpreadsheetReader.CreateFmt(rsFileNotFound, [AFileName]);
// First try to determine file format from the extension
if GetFormatFromFileName(AFilename, fmtID) then begin
try
ReadFromFile(AFileName, fmtID, APassword, AParams);
exit;
except
// format does not match. We must continue with rest of procedure
end;
end else
fmtID := MaxInt;
// Try to get file format from file header // Try to get file format from file header
GetFormatFromFileHeader(AFileName, fileformats); GetFormatFromFileHeader(AFileName, fileformats);
if Length(fileformats) = 0 then if Length(fileformats) = 0 then
// If not successful use formats defined by extension // If not successful use formats defined by extension
fileFormats := GetSpreadFormatsFromFileName(faRead, AFileName); fileFormats := GetSpreadFormatsFromFileName(faRead, AFileName);
// Remove already tested format
found := false;
i := 0;
while (i <= High(fileFormats)) do begin
if fileFormats[i] = fmtID then begin
found := true;
inc(i);
while (i <= High(fileFormats)) do begin
fileFormats[i-1] := fileFormats[i];
inc(i);
end;
end else
inc(i);
end;
if found then SetLength(fileFormats, Length(fileFormats)-1);
// No file format found for this file --> error // No file format found for this file --> error
if Length(fileformats) = 0 then if Length(fileformats) = 0 then
raise EFPSpreadsheetReader.CreateFmt(rsReaderNotFound, [AFileName]); raise EFPSpreadsheetReader.CreateFmt(rsReaderNotFound, [AFileName]);

View File

@ -1370,7 +1370,7 @@ var
fileformats: TsSpreadFormatIDArray; fileformats: TsSpreadFormatIDArray;
begin begin
fileFormats := GetSpreadFormatsFromFileName(faRead, AFileName, ord(sfExcel8)); fileFormats := GetSpreadFormatsFromFileName(faRead, AFileName, ord(sfExcel8));
Result := (Length(fileFormats) > 0) and (fileFormats[0] <= sfidUnknown); Result := (Length(fileFormats) > 0); // and (fileFormats[0] <= sfidUnknown); // wp - removed for new format detection
if Result then AFormatID := fileFormats[0]; if Result then AFormatID := fileFormats[0];
end; end;