diff --git a/components/fpspreadsheet/examples/other/metadata/demo_metadata.lpr b/components/fpspreadsheet/examples/other/metadata/demo_metadata.lpr
index 50fb7ad07..7ad702e35 100644
--- a/components/fpspreadsheet/examples/other/metadata/demo_metadata.lpr
+++ b/components/fpspreadsheet/examples/other/metadata/demo_metadata.lpr
@@ -58,6 +58,7 @@ end;
var
book: TsWorkbook;
sheet: TsWorksheet;
+ i: Integer;
begin
book := TsWorkbook.Create;
try
@@ -72,6 +73,8 @@ begin
book.MetaData.Comments.Add('Assign the creation date to the field CreatedAt.');
book.MetaData.Keywords.Add('Test');
book.MetaData.Keywords.Add('FPSpreadsheet');
+ book.MetaData.AddCustom('Comparny', 'Disney');
+ book.MetaData.AddCustom('Status', 'finished');
sheet := book.AddWorksheet('Test');
sheet.WriteText(2, 3, 'abc');
@@ -86,9 +89,9 @@ begin
book := TsWorkbook.Create;
try
// Select one of these
- book.ReadFromFile('test.ods');
+// book.ReadFromFile('test.ods');
// book.ReadFromFile('test.xlsx');
-// book.ReadFromFile('test.xml');
+ book.ReadFromFile('test.xml');
WriteLn('Created by : ', book.MetaData.CreatedBy);
WriteLn('Date created : ', DateTimeToStr(book.MetaData.DateCreated));
WriteLn('Modified by : ', book.MetaData.LastModifiedBy);
@@ -96,6 +99,9 @@ begin
WriteLn('Title : ', book.MetaData.Title);
WriteLn('Subject : ', book.MetaData.Subject);
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(book.MetaData.Comments.Text);
finally
diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas
index 0d658a528..6d2fe7ba4 100644
--- a/components/fpspreadsheet/source/common/fpsopendocument.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocument.pas
@@ -1966,6 +1966,7 @@ var
book: TsWorkbook;
nodeName: String;
s: String;
+ name: String;
begin
book := TsWorkbook(FWorkbook);
@@ -1992,6 +1993,12 @@ begin
book.MetaData.Title := s;
'dc:subject':
book.Metadata.Subject := s;
+ 'meta:user-defined':
+ begin
+ name := GetAttrValue(ANode, 'meta:name');
+ if name <> '' then
+ book.MetaData.AddCustom(name, s);
+ end;
end;
ANode := ANode.NextSibling;
end;
@@ -5911,6 +5918,16 @@ begin
'%s', [s]));
end;
+ if book.MetaData.Custom.Count > 0 then
+ begin
+ for i := 0 to book.Metadata.Custom.Count-1 do
+ AppendToStream(FSMeta, Format(
+ '%s', [
+ book.Metadata.Custom.Names[i],
+ book.Metadata.Custom.ValueFromIndex[i]
+ ]));
+ end;
+
AppendToStream(FSMeta,
'');
AppendToStream(FSMeta,
diff --git a/components/fpspreadsheet/source/common/fpstypes.pas b/components/fpspreadsheet/source/common/fpstypes.pas
index 20e6afb0b..447fa00c3 100644
--- a/components/fpspreadsheet/source/common/fpstypes.pas
+++ b/components/fpspreadsheet/source/common/fpstypes.pas
@@ -976,9 +976,12 @@ type
FSubject: String;
FComments: TStrings;
FKeywords: TStrings;
+ FCustom: TStrings;
public
constructor Create;
destructor Destroy; override;
+ function AddCustom(AName, AValue: String): Integer;
+ procedure Clear;
function IsEmpty: Boolean;
property CreatedBy: String read FCreatedBy write FCreatedBy;
property LastModifiedBy: String read FLastModifiedBy write FLastModifiedBy;
@@ -987,6 +990,7 @@ type
property Subject: String read FSubject write FSubject;
property Title: String read FTitle write FTitle;
property Comments: TStrings read FComments write FComments;
+ property Custom: TStrings read FCustom write FCustom;
property Keywords: TStrings read FKeywords write FKeywords;
end;
@@ -1199,20 +1203,44 @@ begin
inherited;
FComments := TStringList.Create;
FKeywords := TStringList.Create;
+ FCustom := TStringList.Create;
end;
destructor TsMetaData.Destroy;
begin
FComments.Free;
FKeywords.Free;
+ FCustom.Free;
inherited;
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;
begin
Result := (FCreatedBy = '') and (FLastModifiedBy = '') 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);
end;
diff --git a/components/fpspreadsheet/source/common/xlsxml.pas b/components/fpspreadsheet/source/common/xlsxml.pas
index f43d6540e..f1c4f06b3 100644
--- a/components/fpspreadsheet/source/common/xlsxml.pas
+++ b/components/fpspreadsheet/source/common/xlsxml.pas
@@ -45,6 +45,7 @@ type
procedure ReadCellProtection(ANode: TDOMNode; var AFormat: TsCellFormat);
procedure ReadComment(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ACell: PCell);
procedure ReadConditionalFormatting(ANode: TDOMNode; AWorksheet: TsBasicWorksheet);
+ procedure ReadCustomDocumentProperties(ANode: TDOMNode);
procedure ReadDocumentProperties(ANode: TDOMNode);
procedure ReadExcelWorkbook(ANode: TDOMNode);
procedure ReadFont(ANode: TDOMNode; var AFormat: TsCellFormat);
@@ -96,6 +97,7 @@ type
procedure WriteConditionalFormat(AStream: TStream; AWorksheet: TsBasicWorksheet;
AFormat: TsConditionalFormat);
procedure WriteConditionalFormatting(AStream: TStream; AWorksheet: TsBasicWorksheet);
+ procedure WriteCustomDocumentProperties(AStream: TStream);
procedure WriteDocumentProperties(AStream: TStream);
procedure WriteExcelWorkbook(AStream: TStream);
procedure WriteNames(AStream: TStream; AWorksheet: TsBasicWorksheet);
@@ -1100,6 +1102,32 @@ begin
sheet.WriteConditionalCellFormat(range, TsCFCondition(condition), op1, op2, fmtIndex);
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.
-------------------------------------------------------------------------------}
@@ -2069,6 +2097,7 @@ begin
// Read meta data
ReadDocumentProperties(doc.DocumentElement.FindNode('DocumentProperties'));
+ ReadCustomDocumentProperties(doc.DocumentElement.FindNode('CustomDocumentProperties'));
// Read style list
ReadStyles(doc.DocumentElement.FindNode('Styles'));
@@ -2680,6 +2709,33 @@ begin
end;
end;
+procedure TsSpreadExcelXMLWriter.WriteCustomDocumentProperties(AStream: TStream);
+{
+ Disney
+ finished
+ }
+var
+ book: TsWorkbook;
+ i: Integer;
+begin
+ book := TsWorkbook(FWorkbook);
+ if book.MetaData.Custom.Count = 0 then
+ exit;
+
+ AppendToStream(AStream, INDENT1 +
+ '' + 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 +
+ '' + LF);
+end;
+
procedure TsSpreadExcelXMLWriter.WriteDateTime(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
var
@@ -2719,8 +2775,6 @@ begin
end;
procedure TsSpreadExcelXMLWriter.WriteDocumentProperties(AStream: TStream);
-const
- LE = LineEnding;
var
sTitle: String;
sSubject: String;
@@ -2733,32 +2787,30 @@ var
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
+ if book.MetaData.IsEmpty then
begin
AppendToStream(AStream, INDENT1 +
- '' + LE);
+ '' + LF);
exit;
end;
if book.MetaData.Title <> '' then
- sTitle := '
' + book.MetaData.Title + '' + LE + INDENT2
+ sTitle := '' + book.MetaData.Title + '' + LF + INDENT2
else
sTitle := '';
if book.MetaData.Subject <> '' then
- sSubject := '' + book.MetaData.Subject + '' + LE + INDENT2
+ sSubject := '' + book.MetaData.Subject + '' + LF + INDENT2
else
sSubject := '';
if book.MetaData.CreatedBy <> '' then
- sAuthor := '' + book.MetaData.CreatedBy + '' + LE + INDENT2
+ sAuthor := '' + book.MetaData.CreatedBy + '' + LF + INDENT2
else
sAuthor := '';
if book.MetaData.LastModifiedBy <> '' then
- sLastAuthor := '' + book.MetaData.LastModifiedBy + '' + LE + INDENT2
+ sLastAuthor := '' + book.MetaData.LastModifiedBy + '' + LF + INDENT2
else
sLastAuthor := '';
@@ -2766,7 +2818,7 @@ begin
if book.MetaData.DateCreated > 0 then begin
dt := book.MetaData.DateCreated + GetLocalTimeOffset / (24*60);
sDateCreated := FormatDateTime(ISO8601FormatExtendedUTC, dt);
- sDateCreated := '' + sDateCreated + '' + LE + INDENT2;
+ sDateCreated := '' + sDateCreated + '' + LF + INDENT2;
end else
sDateCreated := '';
@@ -2774,20 +2826,20 @@ begin
begin
dt := book.MetaData.DateLastModified + GetLocalTimeOffset / (24*60);
sDateLastSaved := FormatDateTime(ISO8601FormatExtendedUTC, dt);
- sDateLastSaved := '' + sDateLastSaved + '' + LE + INDENT2;
+ sDateLastSaved := '' + sDateLastSaved + '' + LF + INDENT2;
end else
sDateLastSaved := '';
AppendToStream(AStream, INDENT1 +
- '' + LE + INDENT2 +
+ '' + LF + INDENT2 +
sTitle +
sSubject +
sAuthor +
sLastAuthor +
sDateCreated +
sDateLastSaved +
- '16.00' + LE + Indent1 +
- '' + LE
+ '16.00' + LF + INDENT1 +
+ '' + LF
);
end;
@@ -3397,9 +3449,11 @@ begin
' xmlns:o="urn:schemas-microsoft-com:office:office"' + LF +
' xmlns:x="urn:schemas-microsoft-com:office:excel"' + 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);
WriteDocumentProperties(AStream);
+ WriteCustomDocumentProperties(AStream);
WriteOfficeDocumentSettings(AStream);
WriteExcelWorkbook(AStream);
WriteStyles(AStream);
diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas
index a9702d7ee..0292176b4 100644
--- a/components/fpspreadsheet/source/common/xlsxooxml.pas
+++ b/components/fpspreadsheet/source/common/xlsxooxml.pas
@@ -173,6 +173,7 @@ type
procedure WriteComments(AWorksheet: TsBasicWorksheet);
procedure WriteConditionalFormat(AStream: TStream; AFormat: TsConditionalFormat; var APriority: Integer);
procedure WriteConditionalFormats(AStream: TStream; AWorksheet: TsBasicWorksheet);
+ procedure WriteCustomMetaData(AStream: TStream);
procedure WriteDefinedNames(AStream: TStream);
procedure WriteDifferentialFormat(AStream: TStream; AFormat: PsCellFormat);
procedure WriteDifferentialFormats(AStream: TStream);
@@ -217,6 +218,7 @@ type
FSWorkbook: TStream;
FSWorkbookRels: TStream;
FSMetaData: TStream;
+ FSCustomMetaData: TStream;
FSStyles: TStream;
FSSharedStrings: TStream;
FSSharedStrings_complete: TStream;
@@ -307,6 +309,7 @@ const
OOXML_PATH_XL_THEME = 'xl/theme/theme1.xml';
OOXML_PATH_XL_MEDIA = 'xl/media/';
OOXML_PATH_DOCPROPS_CORE = 'docProps/core.xml';
+ OOXML_PATH_DOCPROPS_CUSTOM = 'docProps/custom.xml';
{ OOXML schemas constants }
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_DOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument';
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_STYLES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles';
SCHEMAS_STRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
@@ -330,6 +334,7 @@ const
MIME_RELS = 'application/vnd.openxmlformats-package.relationships+xml';
MIME_OFFICEDOCUMENT = 'application/vnd.openxmlformats-officedocument';
MIME_CORE = 'application/vnd.openxmlformats-package.core-properties+xml';
+ MIME_CUSTOM = 'application/vnd.openxmlformats-officedocument.custom-properties+xml';
MIME_SPREADML = MIME_OFFICEDOCUMENT + '.spreadsheetml';
MIME_SHEET = MIME_SPREADML + '.sheet.main+xml';
MIME_WORKSHEET = MIME_SPREADML + '.worksheet+xml';
@@ -2695,9 +2700,11 @@ end;
procedure TsSpreadOOXMLReader.ReadMetaData(ANode: TDOMNode);
var
+ childNode: TDOMNode;
nodeName: string;
book: TsWorkbook;
s: String;
+ name: String;
dt: TDateTime;
fs: TFormatSettings;
begin
@@ -2714,6 +2721,7 @@ begin
nodeName := ANode.NodeName;
s := GetNodeValue(ANode);
case nodeName of
+ // These fields are from "core.xml"
'dc:title':
book.MetaData.Title := s;
'dc:subject':
@@ -2740,6 +2748,21 @@ begin
'dcterms:modified':
if s <> '' then
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;
ANode := ANode.NextSibling;
end;
@@ -3798,6 +3821,18 @@ begin
finally
XMLStream.Free;
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
FreeAndNil(Doc);
@@ -4516,6 +4551,37 @@ begin
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,
+ '');
+
+ id := 2;
+ for i := 0 to book.MetaData.Custom.Count-1 do
+ begin
+ AppendToStream(AStream, Format(
+ '' +
+ '%s' +
+ '', [
+ id, book.MetaData.Custom.Names[i],
+ book.MetaData.Custom.ValueFromIndex[i]
+ ]));
+ inc(id);
+ end;
+
+ AppendToStream(AStream,
+ '');
+end;
+
procedure TsSpreadOOXMLWriter.WriteDimension(AStream: TStream;
AWorksheet: TsBasicWorksheet);
var
@@ -6084,18 +6150,29 @@ begin
{ --- meta data ---- }
WriteMetaData(FSMetaData);
+ WriteCustomMetaData(FSCustomMetaData);
{ --- _rels/.rels --- }
AppendToStream(FSRelsRels,
XML_HEADER + LineEnding);
+
AppendToStream(FSRelsRels, Format(
- '' + LineEnding, [SCHEMAS_RELS]));
- AppendToStream(FSRelsRels, Format(
- '' + LineEnding,
- [SCHEMAS_META_CORE]));
+ '' + LineEnding,
+ [SCHEMAS_RELS]));
+
AppendToStream(FSRelsRels, Format(
'' + LineEnding,
[SCHEMAS_DOCUMENT]));
+
+ AppendToStream(FSRelsRels, Format(
+ '' + LineEnding,
+ [SCHEMAS_META_CORE]));
+
+ if TsWorkbook(FWorkbook).MetaData.Custom.Count > 0 then
+ AppendToStream(FSRelsRels, Format(
+ '' + LineEnding,
+ [SCHEMAS_META_CUSTOM]));
+
AppendToStream(FSRelsRels,
'');
@@ -6378,6 +6455,10 @@ begin
AppendToStream(FSContentTypes,
'');
+ if book.MetaData.Custom.Count > 0 then
+ AppendToStream(FSContentTypes,
+ '');
+
AppendToStream(FSContentTypes,
'');
end;
@@ -6808,6 +6889,7 @@ begin
FSSharedStrings := CreateTempStream(FWorkbook, 'fpsSS');
FSSharedStrings_complete := CreateTempStream(FWorkbook, 'fpsSSC');
FSMetaData := CreateTempStream(FWorkbook, 'fpsMETA');
+ FSCustomMetaData := CreateTempStream(FWorkbook, 'fpsCM');
{
if boFileStream in FWorkbook.Options then
begin
@@ -6848,6 +6930,7 @@ procedure TsSpreadOOXMLWriter.DestroyStreams;
var
stream: TStream;
begin
+ DestroyTempStream(FSCustomMetaData);
DestroyTempStream(FSMetaData);
DestroyTempStream(FSContentTypes);
DestroyTempStream(FSRelsRels);
@@ -6897,6 +6980,7 @@ begin
ResetStream(FSStyles);
ResetStream(FSSharedStrings_complete);
ResetStream(FSMetaData);
+ ResetStream(FSCustomMetaData);
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(FSComments) do ResetStream(FSComments[i]);
@@ -6964,6 +7048,8 @@ begin
if FSSharedStrings_complete.Size > 0 then
FZip.Entries.AddFileEntry(FSSharedStrings_complete, OOXML_PATH_XL_STRINGS);
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
WriteMedia(FZip);