fpspreadsheet: Add custom meta data support. Reading/writing for XLSX, Excel XML, ODS.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7591 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-07-29 22:51:39 +00:00
parent 3959cbbe45
commit 7c09abbb51
5 changed files with 213 additions and 22 deletions

View File

@ -58,6 +58,7 @@ end;
var var
book: TsWorkbook; book: TsWorkbook;
sheet: TsWorksheet; sheet: TsWorksheet;
i: Integer;
begin begin
book := TsWorkbook.Create; book := TsWorkbook.Create;
try try
@ -72,6 +73,8 @@ begin
book.MetaData.Comments.Add('Assign the creation date to the field CreatedAt.'); book.MetaData.Comments.Add('Assign the creation date to the field CreatedAt.');
book.MetaData.Keywords.Add('Test'); book.MetaData.Keywords.Add('Test');
book.MetaData.Keywords.Add('FPSpreadsheet'); book.MetaData.Keywords.Add('FPSpreadsheet');
book.MetaData.AddCustom('Comparny', 'Disney');
book.MetaData.AddCustom('Status', 'finished');
sheet := book.AddWorksheet('Test'); sheet := book.AddWorksheet('Test');
sheet.WriteText(2, 3, 'abc'); sheet.WriteText(2, 3, 'abc');
@ -86,9 +89,9 @@ begin
book := TsWorkbook.Create; book := TsWorkbook.Create;
try try
// Select one of these // Select one of these
book.ReadFromFile('test.ods'); // book.ReadFromFile('test.ods');
// book.ReadFromFile('test.xlsx'); // book.ReadFromFile('test.xlsx');
// book.ReadFromFile('test.xml'); book.ReadFromFile('test.xml');
WriteLn('Created by : ', book.MetaData.CreatedBy); WriteLn('Created by : ', book.MetaData.CreatedBy);
WriteLn('Date created : ', DateTimeToStr(book.MetaData.DateCreated)); WriteLn('Date created : ', DateTimeToStr(book.MetaData.DateCreated));
WriteLn('Modified by : ', book.MetaData.LastModifiedBy); WriteLn('Modified by : ', book.MetaData.LastModifiedBy);
@ -96,6 +99,9 @@ begin
WriteLn('Title : ', book.MetaData.Title); WriteLn('Title : ', book.MetaData.Title);
WriteLn('Subject : ', book.MetaData.Subject); WriteLn('Subject : ', book.MetaData.Subject);
WriteLn('Keywords : ', book.MetaData.Keywords.CommaText); WriteLn('Keywords : ', book.MetaData.Keywords.CommaText);
WriteLn('Custom : ', 'Name':20, 'Value':20);
for i := 0 to book.MetaData.Custom.Count-1 do
WriteLn(' ', book.MetaData.Custom.Names[i]:20, book.MetaData.Custom.ValueFromIndex[i]:20);
WriteLn('Comments: '); WriteLn('Comments: ');
WriteLn(book.MetaData.Comments.Text); WriteLn(book.MetaData.Comments.Text);
finally finally

View File

@ -1966,6 +1966,7 @@ var
book: TsWorkbook; book: TsWorkbook;
nodeName: String; nodeName: String;
s: String; s: String;
name: String;
begin begin
book := TsWorkbook(FWorkbook); book := TsWorkbook(FWorkbook);
@ -1992,6 +1993,12 @@ begin
book.MetaData.Title := s; book.MetaData.Title := s;
'dc:subject': 'dc:subject':
book.Metadata.Subject := s; book.Metadata.Subject := s;
'meta:user-defined':
begin
name := GetAttrValue(ANode, 'meta:name');
if name <> '' then
book.MetaData.AddCustom(name, s);
end;
end; end;
ANode := ANode.NextSibling; ANode := ANode.NextSibling;
end; end;
@ -5911,6 +5918,16 @@ begin
'<dc:description>%s</dc:description>', [s])); '<dc:description>%s</dc:description>', [s]));
end; end;
if book.MetaData.Custom.Count > 0 then
begin
for i := 0 to book.Metadata.Custom.Count-1 do
AppendToStream(FSMeta, Format(
'<meta:user-defined meta:name="%s">%s</meta:user-defined>', [
book.Metadata.Custom.Names[i],
book.Metadata.Custom.ValueFromIndex[i]
]));
end;
AppendToStream(FSMeta, AppendToStream(FSMeta,
'</office:meta>'); '</office:meta>');
AppendToStream(FSMeta, AppendToStream(FSMeta,

View File

@ -976,9 +976,12 @@ type
FSubject: String; FSubject: String;
FComments: TStrings; FComments: TStrings;
FKeywords: TStrings; FKeywords: TStrings;
FCustom: TStrings;
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
function AddCustom(AName, AValue: String): Integer;
procedure Clear;
function IsEmpty: Boolean; function IsEmpty: Boolean;
property CreatedBy: String read FCreatedBy write FCreatedBy; property CreatedBy: String read FCreatedBy write FCreatedBy;
property LastModifiedBy: String read FLastModifiedBy write FLastModifiedBy; property LastModifiedBy: String read FLastModifiedBy write FLastModifiedBy;
@ -987,6 +990,7 @@ type
property Subject: String read FSubject write FSubject; property Subject: String read FSubject write FSubject;
property Title: String read FTitle write FTitle; property Title: String read FTitle write FTitle;
property Comments: TStrings read FComments write FComments; property Comments: TStrings read FComments write FComments;
property Custom: TStrings read FCustom write FCustom;
property Keywords: TStrings read FKeywords write FKeywords; property Keywords: TStrings read FKeywords write FKeywords;
end; end;
@ -1199,20 +1203,44 @@ begin
inherited; inherited;
FComments := TStringList.Create; FComments := TStringList.Create;
FKeywords := TStringList.Create; FKeywords := TStringList.Create;
FCustom := TStringList.Create;
end; end;
destructor TsMetaData.Destroy; destructor TsMetaData.Destroy;
begin begin
FComments.Free; FComments.Free;
FKeywords.Free; FKeywords.Free;
FCustom.Free;
inherited; inherited;
end; end;
procedure TsMetaData.Clear;
begin
FTitle := '';
FSubject := '';
FCreatedBy := '';
FLastModifiedBy := '';
FDateCreated := 0;
FDateLastModified := 0;
FComments.Clear;
FKeywords.Clear;
FCustom.Clear;
end;
function TsMetaData.AddCustom(AName, AValue: String): Integer;
begin
Result := FCustom.IndexOf(AName);
if result > -1 then
FCustom.ValueFromIndex[Result] := AValue
else
Result := FCustom.Add(AName + '=' + AValue);
end;
function TsMetaData.IsEmpty: Boolean; function TsMetaData.IsEmpty: Boolean;
begin begin
Result := (FCreatedBy = '') and (FLastModifiedBy = '') and Result := (FCreatedBy = '') and (FLastModifiedBy = '') and
(FTitle = '') and (FSubject = '') and (FTitle = '') and (FSubject = '') and
(FComments.Count = 0) and (FKeywords.Count = 0) and (FComments.Count = 0) and (FKeywords.Count = 0) and (FCustom.Count = 0) and
(FDateCreated = 0) and (FDateLastModified = 0); (FDateCreated = 0) and (FDateLastModified = 0);
end; end;

View File

@ -45,6 +45,7 @@ type
procedure ReadCellProtection(ANode: TDOMNode; var AFormat: TsCellFormat); procedure ReadCellProtection(ANode: TDOMNode; var AFormat: TsCellFormat);
procedure ReadComment(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ACell: PCell); procedure ReadComment(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ACell: PCell);
procedure ReadConditionalFormatting(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); procedure ReadConditionalFormatting(ANode: TDOMNode; AWorksheet: TsBasicWorksheet);
procedure ReadCustomDocumentProperties(ANode: TDOMNode);
procedure ReadDocumentProperties(ANode: TDOMNode); procedure ReadDocumentProperties(ANode: TDOMNode);
procedure ReadExcelWorkbook(ANode: TDOMNode); procedure ReadExcelWorkbook(ANode: TDOMNode);
procedure ReadFont(ANode: TDOMNode; var AFormat: TsCellFormat); procedure ReadFont(ANode: TDOMNode; var AFormat: TsCellFormat);
@ -96,6 +97,7 @@ type
procedure WriteConditionalFormat(AStream: TStream; AWorksheet: TsBasicWorksheet; procedure WriteConditionalFormat(AStream: TStream; AWorksheet: TsBasicWorksheet;
AFormat: TsConditionalFormat); AFormat: TsConditionalFormat);
procedure WriteConditionalFormatting(AStream: TStream; AWorksheet: TsBasicWorksheet); procedure WriteConditionalFormatting(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteCustomDocumentProperties(AStream: TStream);
procedure WriteDocumentProperties(AStream: TStream); procedure WriteDocumentProperties(AStream: TStream);
procedure WriteExcelWorkbook(AStream: TStream); procedure WriteExcelWorkbook(AStream: TStream);
procedure WriteNames(AStream: TStream; AWorksheet: TsBasicWorksheet); procedure WriteNames(AStream: TStream; AWorksheet: TsBasicWorksheet);
@ -1100,6 +1102,32 @@ begin
sheet.WriteConditionalCellFormat(range, TsCFCondition(condition), op1, op2, fmtIndex); sheet.WriteConditionalCellFormat(range, TsCFCondition(condition), op1, op2, fmtIndex);
end; end;
{@@ ----------------------------------------------------------------------------
Read the custom meta data fields
-------------------------------------------------------------------------------}
procedure TsSpreadExcelXMLReader.ReadCustomDocumentProperties(ANode: TDOMNode);
var
book: TsWorkbook;
value: String;
nodeName: String;
begin
if ANode = nil then
exit;
book := TsWorkbook(FWorkbook);
ANode := ANode.FirstChild;
while ANode <> nil do
begin
nodeName := ANode.NodeName;
if nodeName <> '#text' then
begin
value := GetNodeValue(ANode);
book.MetaData.AddCustom(nodeName, value);
end;
ANode := ANode.NextSibling;
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Reads the meta data etc. Reads the meta data etc.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -2069,6 +2097,7 @@ begin
// Read meta data // Read meta data
ReadDocumentProperties(doc.DocumentElement.FindNode('DocumentProperties')); ReadDocumentProperties(doc.DocumentElement.FindNode('DocumentProperties'));
ReadCustomDocumentProperties(doc.DocumentElement.FindNode('CustomDocumentProperties'));
// Read style list // Read style list
ReadStyles(doc.DocumentElement.FindNode('Styles')); ReadStyles(doc.DocumentElement.FindNode('Styles'));
@ -2680,6 +2709,33 @@ begin
end; end;
end; end;
procedure TsSpreadExcelXMLWriter.WriteCustomDocumentProperties(AStream: TStream);
{ <CustomDocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Comparny dt:dt="string">Disney</Comparny>
<Status dt:dt="string">finished</Status>
</CustomDocumentProperties> }
var
book: TsWorkbook;
i: Integer;
begin
book := TsWorkbook(FWorkbook);
if book.MetaData.Custom.Count = 0 then
exit;
AppendToStream(AStream, INDENT1 +
'<CustomDocumentProperties xmlns="urn:schemas-microsoft-com:office:office">' + LF);
for i := 0 to book.MetaData.Custom.Count-1 do
AppendToStream(AStream, Format(INDENT2 +
'<%0:s dt:dt="string">%1:s</%0:s>' + LF, [
book.MetaData.Custom.Names[i],
book.MetaData.Custom.ValueFromIndex[i]
]));
AppendToStream(AStream, INDENT1 +
'</CustomDocumentProperties>' + LF);
end;
procedure TsSpreadExcelXMLWriter.WriteDateTime(AStream: TStream; procedure TsSpreadExcelXMLWriter.WriteDateTime(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
var var
@ -2719,8 +2775,6 @@ begin
end; end;
procedure TsSpreadExcelXMLWriter.WriteDocumentProperties(AStream: TStream); procedure TsSpreadExcelXMLWriter.WriteDocumentProperties(AStream: TStream);
const
LE = LineEnding;
var var
sTitle: String; sTitle: String;
sSubject: String; sSubject: String;
@ -2733,32 +2787,30 @@ var
begin begin
book := TsWorkbook(FWorkbook); book := TsWorkbook(FWorkbook);
if (book.MetaData.Title = '') and if book.MetaData.IsEmpty then
(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" />' + LE); '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office" />' + LF);
exit; exit;
end; end;
if book.MetaData.Title <> '' then if book.MetaData.Title <> '' then
sTitle := '<Title>' + book.MetaData.Title + '</Title>' + LE + INDENT2 sTitle := '<Title>' + book.MetaData.Title + '</Title>' + LF + INDENT2
else else
sTitle := ''; sTitle := '';
if book.MetaData.Subject <> '' then if book.MetaData.Subject <> '' then
sSubject := '<Subject>' + book.MetaData.Subject + '</Subject>' + LE + INDENT2 sSubject := '<Subject>' + book.MetaData.Subject + '</Subject>' + LF + INDENT2
else else
sSubject := ''; sSubject := '';
if book.MetaData.CreatedBy <> '' then if book.MetaData.CreatedBy <> '' then
sAuthor := '<Author>' + book.MetaData.CreatedBy + '</Author>' + LE + INDENT2 sAuthor := '<Author>' + book.MetaData.CreatedBy + '</Author>' + LF + INDENT2
else else
sAuthor := ''; sAuthor := '';
if book.MetaData.LastModifiedBy <> '' then if book.MetaData.LastModifiedBy <> '' then
sLastAuthor := '<LastAuthor>' + book.MetaData.LastModifiedBy + '</LastAuthor>' + LE + INDENT2 sLastAuthor := '<LastAuthor>' + book.MetaData.LastModifiedBy + '</LastAuthor>' + LF + INDENT2
else else
sLastAuthor := ''; sLastAuthor := '';
@ -2766,7 +2818,7 @@ begin
if book.MetaData.DateCreated > 0 then begin if book.MetaData.DateCreated > 0 then begin
dt := book.MetaData.DateCreated + GetLocalTimeOffset / (24*60); dt := book.MetaData.DateCreated + GetLocalTimeOffset / (24*60);
sDateCreated := FormatDateTime(ISO8601FormatExtendedUTC, dt); sDateCreated := FormatDateTime(ISO8601FormatExtendedUTC, dt);
sDateCreated := '<Created>' + sDateCreated + '</Created>' + LE + INDENT2; sDateCreated := '<Created>' + sDateCreated + '</Created>' + LF + INDENT2;
end else end else
sDateCreated := ''; sDateCreated := '';
@ -2774,20 +2826,20 @@ begin
begin begin
dt := book.MetaData.DateLastModified + GetLocalTimeOffset / (24*60); dt := book.MetaData.DateLastModified + GetLocalTimeOffset / (24*60);
sDateLastSaved := FormatDateTime(ISO8601FormatExtendedUTC, dt); sDateLastSaved := FormatDateTime(ISO8601FormatExtendedUTC, dt);
sDateLastSaved := '<LastSaved>' + sDateLastSaved + '</LastSaved>' + LE + INDENT2; sDateLastSaved := '<LastSaved>' + sDateLastSaved + '</LastSaved>' + LF + INDENT2;
end else end else
sDateLastSaved := ''; sDateLastSaved := '';
AppendToStream(AStream, INDENT1 + AppendToStream(AStream, INDENT1 +
'<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">' + LE + INDENT2 + '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">' + LF + INDENT2 +
sTitle + sTitle +
sSubject + sSubject +
sAuthor + sAuthor +
sLastAuthor + sLastAuthor +
sDateCreated + sDateCreated +
sDateLastSaved + sDateLastSaved +
'<Version>16.00</Version>' + LE + Indent1 + '<Version>16.00</Version>' + LF + INDENT1 +
'</DocumentProperties>' + LE '</DocumentProperties>' + LF
); );
end; end;
@ -3397,9 +3449,11 @@ begin
' xmlns:o="urn:schemas-microsoft-com:office:office"' + LF + ' xmlns:o="urn:schemas-microsoft-com:office:office"' + LF +
' xmlns:x="urn:schemas-microsoft-com:office:excel"' + LF + ' xmlns:x="urn:schemas-microsoft-com:office:excel"' + LF +
' xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"' + LF + ' xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"' + LF +
' xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"' + LF +
' xmlns:html="http://www.w3.org/TR/REC-html40">' + LF); ' xmlns:html="http://www.w3.org/TR/REC-html40">' + LF);
WriteDocumentProperties(AStream); WriteDocumentProperties(AStream);
WriteCustomDocumentProperties(AStream);
WriteOfficeDocumentSettings(AStream); WriteOfficeDocumentSettings(AStream);
WriteExcelWorkbook(AStream); WriteExcelWorkbook(AStream);
WriteStyles(AStream); WriteStyles(AStream);

View File

@ -173,6 +173,7 @@ type
procedure WriteComments(AWorksheet: TsBasicWorksheet); procedure WriteComments(AWorksheet: TsBasicWorksheet);
procedure WriteConditionalFormat(AStream: TStream; AFormat: TsConditionalFormat; var APriority: Integer); procedure WriteConditionalFormat(AStream: TStream; AFormat: TsConditionalFormat; var APriority: Integer);
procedure WriteConditionalFormats(AStream: TStream; AWorksheet: TsBasicWorksheet); procedure WriteConditionalFormats(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteCustomMetaData(AStream: TStream);
procedure WriteDefinedNames(AStream: TStream); procedure WriteDefinedNames(AStream: TStream);
procedure WriteDifferentialFormat(AStream: TStream; AFormat: PsCellFormat); procedure WriteDifferentialFormat(AStream: TStream; AFormat: PsCellFormat);
procedure WriteDifferentialFormats(AStream: TStream); procedure WriteDifferentialFormats(AStream: TStream);
@ -217,6 +218,7 @@ type
FSWorkbook: TStream; FSWorkbook: TStream;
FSWorkbookRels: TStream; FSWorkbookRels: TStream;
FSMetaData: TStream; FSMetaData: TStream;
FSCustomMetaData: TStream;
FSStyles: TStream; FSStyles: TStream;
FSSharedStrings: TStream; FSSharedStrings: TStream;
FSSharedStrings_complete: TStream; FSSharedStrings_complete: TStream;
@ -307,6 +309,7 @@ const
OOXML_PATH_XL_THEME = 'xl/theme/theme1.xml'; OOXML_PATH_XL_THEME = 'xl/theme/theme1.xml';
OOXML_PATH_XL_MEDIA = 'xl/media/'; OOXML_PATH_XL_MEDIA = 'xl/media/';
OOXML_PATH_DOCPROPS_CORE = 'docProps/core.xml'; OOXML_PATH_DOCPROPS_CORE = 'docProps/core.xml';
OOXML_PATH_DOCPROPS_CUSTOM = 'docProps/custom.xml';
{ OOXML schemas constants } { OOXML schemas constants }
SCHEMAS_TYPES = 'http://schemas.openxmlformats.org/package/2006/content-types'; SCHEMAS_TYPES = 'http://schemas.openxmlformats.org/package/2006/content-types';
@ -314,6 +317,7 @@ const
SCHEMAS_DOC_RELS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'; SCHEMAS_DOC_RELS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
SCHEMAS_DOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument'; SCHEMAS_DOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument';
SCHEMAS_META_CORE = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties'; SCHEMAS_META_CORE = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
SCHEMAS_META_CUSTOM = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties';
SCHEMAS_WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet'; SCHEMAS_WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
SCHEMAS_STYLES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles'; SCHEMAS_STYLES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles';
SCHEMAS_STRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings'; SCHEMAS_STRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
@ -330,6 +334,7 @@ const
MIME_RELS = 'application/vnd.openxmlformats-package.relationships+xml'; MIME_RELS = 'application/vnd.openxmlformats-package.relationships+xml';
MIME_OFFICEDOCUMENT = 'application/vnd.openxmlformats-officedocument'; MIME_OFFICEDOCUMENT = 'application/vnd.openxmlformats-officedocument';
MIME_CORE = 'application/vnd.openxmlformats-package.core-properties+xml'; MIME_CORE = 'application/vnd.openxmlformats-package.core-properties+xml';
MIME_CUSTOM = 'application/vnd.openxmlformats-officedocument.custom-properties+xml';
MIME_SPREADML = MIME_OFFICEDOCUMENT + '.spreadsheetml'; MIME_SPREADML = MIME_OFFICEDOCUMENT + '.spreadsheetml';
MIME_SHEET = MIME_SPREADML + '.sheet.main+xml'; MIME_SHEET = MIME_SPREADML + '.sheet.main+xml';
MIME_WORKSHEET = MIME_SPREADML + '.worksheet+xml'; MIME_WORKSHEET = MIME_SPREADML + '.worksheet+xml';
@ -2695,9 +2700,11 @@ end;
procedure TsSpreadOOXMLReader.ReadMetaData(ANode: TDOMNode); procedure TsSpreadOOXMLReader.ReadMetaData(ANode: TDOMNode);
var var
childNode: TDOMNode;
nodeName: string; nodeName: string;
book: TsWorkbook; book: TsWorkbook;
s: String; s: String;
name: String;
dt: TDateTime; dt: TDateTime;
fs: TFormatSettings; fs: TFormatSettings;
begin begin
@ -2714,6 +2721,7 @@ begin
nodeName := ANode.NodeName; nodeName := ANode.NodeName;
s := GetNodeValue(ANode); s := GetNodeValue(ANode);
case nodeName of case nodeName of
// These fields are from "core.xml"
'dc:title': 'dc:title':
book.MetaData.Title := s; book.MetaData.Title := s;
'dc:subject': 'dc:subject':
@ -2740,6 +2748,21 @@ begin
'dcterms:modified': 'dcterms:modified':
if s <> '' then if s <> '' then
book.MetaData.DateLastModified :=ISO8601StrToDateTime(s); book.MetaData.DateLastModified :=ISO8601StrToDateTime(s);
// This field is from "custom.xml"
'property':
begin
name := GetAttrValue(ANode, 'name');
childNode := ANode.Firstchild;
while childNode <> nil do
begin
nodeName := childNode.NodeName;
s := GetNodeValue(childNode);
if (s <> '') then
book.MetaData.AddCustom(name, s);
break;
end;
end;
end; end;
ANode := ANode.NextSibling; ANode := ANode.NextSibling;
end; end;
@ -3798,6 +3821,18 @@ begin
finally finally
XMLStream.Free; XMLStream.Free;
end; end;
// custom meta data
XMLStream := CreateXMLStream;
try
if UnzipToStream(AStream, OOXML_PATH_DOCPROPS_CUSTOM, XMLStream) then
begin
ReadXMLStream(Doc, XMLStream);
ReadMetaData(Doc.DocumentElement);
FreeAndNil(Doc);
end;
finally
XMLStream.Free;
end;
finally finally
FreeAndNil(Doc); FreeAndNil(Doc);
@ -4516,6 +4551,37 @@ begin
end; end;
end; end;
procedure TsSpreadOOXMLWriter.WriteCustomMetaData(AStream: TStream);
var
book: TsWorkbook;
i: Integer;
id: Integer;
begin
book := TsWorkbook(FWorkbook);
if book.MetaData.Custom.Count = 0 then
exit;
AppendToStream(AStream,
'<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" ' +
'xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">');
id := 2;
for i := 0 to book.MetaData.Custom.Count-1 do
begin
AppendToStream(AStream, Format(
'<property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="%d" name="%s">' +
'<vt:lpwstr>%s</vt:lpwstr>' +
'</property>', [
id, book.MetaData.Custom.Names[i],
book.MetaData.Custom.ValueFromIndex[i]
]));
inc(id);
end;
AppendToStream(AStream,
'</Properties>');
end;
procedure TsSpreadOOXMLWriter.WriteDimension(AStream: TStream; procedure TsSpreadOOXMLWriter.WriteDimension(AStream: TStream;
AWorksheet: TsBasicWorksheet); AWorksheet: TsBasicWorksheet);
var var
@ -6084,18 +6150,29 @@ begin
{ --- meta data ---- } { --- meta data ---- }
WriteMetaData(FSMetaData); WriteMetaData(FSMetaData);
WriteCustomMetaData(FSCustomMetaData);
{ --- _rels/.rels --- } { --- _rels/.rels --- }
AppendToStream(FSRelsRels, AppendToStream(FSRelsRels,
XML_HEADER + LineEnding); XML_HEADER + LineEnding);
AppendToStream(FSRelsRels, Format( AppendToStream(FSRelsRels, Format(
'<Relationships xmlns="%s">' + LineEnding, [SCHEMAS_RELS])); '<Relationships xmlns="%s">' + LineEnding,
AppendToStream(FSRelsRels, Format( [SCHEMAS_RELS]));
'<Relationship Id="rId2" Target="docProps/core.xml" Type="%s" />' + LineEnding,
[SCHEMAS_META_CORE]));
AppendToStream(FSRelsRels, Format( AppendToStream(FSRelsRels, Format(
'<Relationship Id="rId1" Target="xl/workbook.xml" Type="%s" />' + LineEnding, '<Relationship Id="rId1" Target="xl/workbook.xml" Type="%s" />' + LineEnding,
[SCHEMAS_DOCUMENT])); [SCHEMAS_DOCUMENT]));
AppendToStream(FSRelsRels, Format(
'<Relationship Id="rId2" Target="docProps/core.xml" Type="%s" />' + LineEnding,
[SCHEMAS_META_CORE]));
if TsWorkbook(FWorkbook).MetaData.Custom.Count > 0 then
AppendToStream(FSRelsRels, Format(
'<Relationship Id="rId3" Target="docProps/custom.xml" Type="%s" />' + LineEnding,
[SCHEMAS_META_CUSTOM]));
AppendToStream(FSRelsRels, AppendToStream(FSRelsRels,
'</Relationships>'); '</Relationships>');
@ -6378,6 +6455,10 @@ begin
AppendToStream(FSContentTypes, AppendToStream(FSContentTypes,
'<Override PartName="/docProps/core.xml" ContentType="' + MIME_CORE + '" />'); '<Override PartName="/docProps/core.xml" ContentType="' + MIME_CORE + '" />');
if book.MetaData.Custom.Count > 0 then
AppendToStream(FSContentTypes,
'<Override PartName="/docProps/custom.xml" ContentType="' + MIME_CUSTOM + '" />');
AppendToStream(FSContentTypes, AppendToStream(FSContentTypes,
'</Types>'); '</Types>');
end; end;
@ -6808,6 +6889,7 @@ begin
FSSharedStrings := CreateTempStream(FWorkbook, 'fpsSS'); FSSharedStrings := CreateTempStream(FWorkbook, 'fpsSS');
FSSharedStrings_complete := CreateTempStream(FWorkbook, 'fpsSSC'); FSSharedStrings_complete := CreateTempStream(FWorkbook, 'fpsSSC');
FSMetaData := CreateTempStream(FWorkbook, 'fpsMETA'); FSMetaData := CreateTempStream(FWorkbook, 'fpsMETA');
FSCustomMetaData := CreateTempStream(FWorkbook, 'fpsCM');
{ {
if boFileStream in FWorkbook.Options then if boFileStream in FWorkbook.Options then
begin begin
@ -6848,6 +6930,7 @@ procedure TsSpreadOOXMLWriter.DestroyStreams;
var var
stream: TStream; stream: TStream;
begin begin
DestroyTempStream(FSCustomMetaData);
DestroyTempStream(FSMetaData); DestroyTempStream(FSMetaData);
DestroyTempStream(FSContentTypes); DestroyTempStream(FSContentTypes);
DestroyTempStream(FSRelsRels); DestroyTempStream(FSRelsRels);
@ -6897,6 +6980,7 @@ begin
ResetStream(FSStyles); ResetStream(FSStyles);
ResetStream(FSSharedStrings_complete); ResetStream(FSSharedStrings_complete);
ResetStream(FSMetaData); ResetStream(FSMetaData);
ResetStream(FSCustomMetaData);
for i:=0 to High(FSSheets) do ResetStream(FSSheets[i]); for i:=0 to High(FSSheets) do ResetStream(FSSheets[i]);
for i:=0 to High(FSSheetRels) do ResetStream(FSSheetRels[i]); for i:=0 to High(FSSheetRels) do ResetStream(FSSheetRels[i]);
for i:=0 to High(FSComments) do ResetStream(FSComments[i]); for i:=0 to High(FSComments) do ResetStream(FSComments[i]);
@ -6964,6 +7048,8 @@ begin
if FSSharedStrings_complete.Size > 0 then if FSSharedStrings_complete.Size > 0 then
FZip.Entries.AddFileEntry(FSSharedStrings_complete, OOXML_PATH_XL_STRINGS); FZip.Entries.AddFileEntry(FSSharedStrings_complete, OOXML_PATH_XL_STRINGS);
FZip.Entries.AddFileEntry(FSMetaData, OOXML_PATH_DOCPROPS_CORE); FZip.Entries.AddFileEntry(FSMetaData, OOXML_PATH_DOCPROPS_CORE);
if TsWorkbook(FWorkbook).MetaData.Custom.Count > 0 then
FZip.Entries.AddFileEntry(FSCustomMetaData, OOXML_PATH_DOCPROPS_CUSTOM);
// Write embedded images // Write embedded images
WriteMedia(FZip); WriteMedia(FZip);