fpspreadsheet: Add metadata writer for Excel XML. Fix usage of UTC in the Excel metadata.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7580 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-07-28 17:57:32 +00:00
parent ed025ddea4
commit ea91b7fca5
4 changed files with 106 additions and 47 deletions

View File

@ -5,7 +5,7 @@ uses
windows, windows,
{$ENDIF} {$ENDIF}
SysUtils, SysUtils,
fpspreadsheet, fpstypes, xlsxooxml, fpsopendocument; fpspreadsheet, fpstypes, xlsxooxml, fpsopendocument, xlsxml;
function GetUserName: String; function GetUserName: String;
// http://forum.lazarus.freepascal.org/index.php/topic,23171.msg138057.html#msg138057 // http://forum.lazarus.freepascal.org/index.php/topic,23171.msg138057.html#msg138057
@ -76,7 +76,8 @@ begin
sheet.WriteText(2, 3, 'abc'); sheet.WriteText(2, 3, 'abc');
sheet.WriteBackgroundColor(2, 3, scYellow); sheet.WriteBackgroundColor(2, 3, scYellow);
book.WriteToFile('test.xlsx', true); book.WriteToFile('test.xlsx', true);
book.WritetoFile('test.ods', true); book.WriteToFile('test.ods', true);
book.WriteToFile('test.xml', true)
finally finally
book.Free; book.Free;
end; end;

View File

@ -41,6 +41,8 @@ const
ISO8601Format='yyyymmdd"T"hhmmss'; ISO8601Format='yyyymmdd"T"hhmmss';
{@@ Extended ISO 8601 date/time format, used in e.g. ODF/opendocument } {@@ Extended ISO 8601 date/time format, used in e.g. ODF/opendocument }
ISO8601FormatExtended='yyyy"-"mm"-"dd"T"hh":"mm":"ss'; ISO8601FormatExtended='yyyy"-"mm"-"dd"T"hh":"mm":"ss';
{@@ Extended ISO 8601 date/time format, as UTC }
ISO8601FormatExtendedUTC='yyyy"-"mm"-"dd"T"hh":"mm":"ss"Z"';
{@@ ISO 8601 date-only format, used in ODF/opendocument } {@@ ISO 8601 date-only format, used in ODF/opendocument }
ISO8601FormatDateOnly='yyyy"-"mm"-"dd'; ISO8601FormatDateOnly='yyyy"-"mm"-"dd';
{@@ ISO 8601 time-only format, used in ODF/opendocument } {@@ ISO 8601 time-only format, used in ODF/opendocument }
@ -3147,6 +3149,7 @@ end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Converts an ISO8601-formatted date/time to TDateTime Converts an ISO8601-formatted date/time to TDateTime
Assumes UTC when the input string ends with 'Z' and converts to local time.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function ISO8601StrToDateTime(s: String): TDateTime; function ISO8601StrToDateTime(s: String): TDateTime;
// example: 2020-07-28T11:07:36Z // example: 2020-07-28T11:07:36Z
@ -3157,6 +3160,7 @@ var
hours, mins, days: integer; hours, mins, days: integer;
secs: Double; secs: Double;
hrPos, minPos, secPos: integer; hrPos, minPos, secPos: integer;
ms: Double;
begin begin
Result := 0; Result := 0;
@ -3180,12 +3184,13 @@ begin
s[p] := ' '; s[p] := ' ';
// Strip milliseconds? // Strip milliseconds?
p := Pos('.', s); p := Pos('.', s);
if (p > 1) then if (p > 1) then begin
s := Copy(s, 1, p-1); ms := StrToFloat('0' + Copy(s, p, MaxInt), fs);
Result := StrToDateTime(s, fs); end else
exit; ms := 0;
end; Result := StrToDateTime(s, fs) + ms;
end else
begin
p := pos('PT', s); p := pos('PT', s);
if p = 1 then if p = 1 then
begin begin
@ -3213,10 +3218,12 @@ begin
days := hours div 24; days := hours div 24;
hours := hours mod 24; hours := hours mod 24;
Result := days + (hours + (mins + secs/60) / 60) / 24; Result := days + (hours + (mins + secs/60) / 60) / 24;
end else
exit;
end; end;
if isUTC then if isUTC then
Result := Result + GetLocalTimeOffset / (60*24); Result := Result - GetLocalTimeOffset / MinsPerDay;
end; end;

View File

@ -2660,21 +2660,69 @@ begin
end; end;
procedure TsSpreadExcelXMLWriter.WriteDocumentProperties(AStream: TStream); procedure TsSpreadExcelXMLWriter.WriteDocumentProperties(AStream: TStream);
const
LE = LineEnding;
var
sTitle: String;
sAuthor: String;
sLastAuthor: String;
sDateCreated: String;
sDateLastSaved: String;
book: TsWorkbook;
dt: TDateTime;
begin
book := TsWorkbook(FWorkbook);
if (book.MetaData.Title = '') and
(book.MetaData.CreatedBy = '') and (book.MetaData.LastModifiedBy = '') and
(book.MetaData.DateCreated <= 0) and (book.MetaData.DateLastModified <= 0) then
begin begin
AppendToStream(AStream, INDENT1 + AppendToStream(AStream, INDENT1 +
'<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office" />' + LineEnding); '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office" />' + LE);
exit;
end;
// replace by these when fpspreadsheet supports these meta data. if book.MetaData.Title <> '' then
{ sTitle := '<Title>' + book.MetaData.Title + '</Title>' + LE + INDENT2
AppendToSstream(AStream, INDENT1 + else
'<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">' + LineEnding + INDENT2 + sTitle := '';
'<Author></Author>' + LineEnding + INDENT2 +
'<LastAuthor></LastAuthor>' + LineEnding + INDENT2 + if book.MetaData.CreatedBy <> '' then
'<Created></Created>' + LineEnding + Indent2 + // Date in format YYYY-mm-ddThh:nn:ssZ sAuthor := '<Author>' + book.MetaData.CreatedBy + '</Author>' + LE + INDENT2
'<Version>16.00</Version>' + LineEnding + Indent1 + else
'</DocumentProperties>' + LineEnding sAuthor := '';
if book.MetaData.LastModifiedBy <> '' then
sLastAuthor := '<LastAuthor>' + book.MetaData.LastModifiedBy + '</LastAuthor>' + LE + INDENT2
else
sLastAuthor := '';
// Dates are UTC and in format YYYY-mm-ddThh:nn:ssZ
if book.MetaData.DateCreated > 0 then begin
dt := book.MetaData.DateCreated + GetLocalTimeOffset / (24*60);
sDateCreated := FormatDateTime(ISO8601FormatExtendedUTC, dt);
sDateCreated := '<Created>' + sDateCreated + '</Created>' + LE + INDENT2;
end else
sDateCreated := '';
if book.MetaData.DateLastModified > 0 then
begin
dt := book.MetaData.DateLastModified + GetLocalTimeOffset / (24*60);
sDateLastSaved := FormatDateTime(ISO8601FormatExtendedUTC, dt);
sDateLastSaved := '<LastSaved>' + sDateLastSaved + '</LastSaved>' + LE + INDENT2;
end else
sDateLastSaved := '';
AppendToStream(AStream, INDENT1 +
'<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">' + LE + INDENT2 +
sTitle +
sAuthor +
sLastAuthor +
sDateCreated +
sDateLastSaved +
'<Version>16.00</Version>' + LE + Indent1 +
'</DocumentProperties>' + LE
); );
}
end; end;
procedure TsSpreadExcelXMLWriter.WriteError(AStream: TStream; procedure TsSpreadExcelXMLWriter.WriteError(AStream: TStream;

View File

@ -6101,6 +6101,7 @@ procedure TsSpreadOOXMLWriter.WriteMetaData(AStream: TStream);
var var
book: TsWorkbook; book: TsWorkbook;
s: String; s: String;
dt: TDateTime;
begin begin
book := TsWorkbook(FWorkbook); book := TsWorkbook(FWorkbook);
@ -6152,15 +6153,17 @@ begin
if book.MetaData.DateCreated > 0 then if book.MetaData.DateCreated > 0 then
begin begin
s := FormatDateTime(ISO8601FormatExtended, book.MetaData.DateCreated) + 'Z'; dt := book.MetaData.DateCreated + GetLocalTimeOffset / MinsPerDay;
s := FormatDateTime(ISO8601FormatExtendedUTC, dt);
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<dcterms:created xsi:type="dcterms:W3CDTF">%s</dcterms:created>', [s])); '<dcterms:created xsi:type="dcterms:W3CDTF">%s</dcterms:created>', [s]));
end; end;
if book.MetaData.DateLastModified <= 0 then if book.MetaData.DateLastModified <= 0 then
s := FormatDateTime(ISO8601FormatExtended, book.MetaData.DateCreated) + 'Z' dt := book.MetaData.DateCreated + GetLocalTimeOffset / MinsPerDay
else else
s := FormatDateTime(ISO8601FormatExtended, book.MetaData.DateLastModified) + 'Z'; dt := book.MetaData.DateLastModified + GetLocalTimeOffset / MinsPerDay;
s := FormatDateTime(ISO8601FormatExtendedUTC, dt);
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<dcterms:modified xsi:type="dcterms:W3CDTF">%s</dcterms:modified>', [s])); '<dcterms:modified xsi:type="dcterms:W3CDTF">%s</dcterms:modified>', [s]));