{ xlsxooxml.pas Writes an OOXML (Office Open XML) document An OOXML document is a compressed ZIP file with the following files inside: [Content_Types].xml - _rels\.rels - xl\_rels\workbook.xml.rels - xl\workbook.xml - Global workbook data and list of worksheets xl\styles.xml - xl\sharedStrings.xml - xl\worksheets\sheet1.xml - Contents of each worksheet ... xl\worksheets\sheetN.xml Specifications obtained from: http://openxmldeveloper.org/default.aspx AUTHORS: Felipe Monteiro de Carvalho } unit xlsxooxml; {$ifdef fpc} {$mode delphi} {$endif} interface uses Classes, SysUtils, fpszipper, {NOTE: fpszipper is the latest zipper.pp Change to standard zipper when FPC 2.4 is released } fpspreadsheet; type { TsSpreadOOXMLWriter } TsSpreadOOXMLWriter = class(TsCustomSpreadWriter) protected { Strings with the contents of files } FContentTypes: string; FRelsRels: string; FWorkbook, FWorkbookRels, FStyles, FSharedStrings: string; FSheets: array of string; FSharedStringsCount: Integer; { Streams with the contents of files } FSContentTypes: TStringStream; FSRelsRels: TStringStream; FSWorkbook, FSWorkbookRels, FSStyles, FSSharedStrings: TStringStream; FSSheets: array of TStringStream; { Routines to write those files } procedure WriteGlobalFiles; procedure WriteContent(AData: TsWorkbook); procedure WriteWorksheet(CurSheet: TsWorksheet); public destructor Destroy; override; { General writing methods } procedure WriteStringToFile(AFileName, AString: string); procedure WriteToFile(const AFileName: string; AData: TsWorkbook; const AOverwriteExisting: Boolean = False); override; procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override; { Record writing methods } procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string; ACell: PCell); override; procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; end; implementation const { OOXML general XML constants } XML_HEADER = ''; { OOXML Directory structure constants } OOXML_PATH_TYPES = '[Content_Types].xml'; OOXML_PATH_RELS = '_rels' + PathDelim; OOXML_PATH_RELS_RELS = '_rels' + PathDelim + '.rels'; OOXML_PATH_XL = 'xl' + PathDelim; OOXML_PATH_XL_RELS = 'xl' + PathDelim + '_rels' + PathDelim; OOXML_PATH_XL_RELS_RELS = 'xl' + PathDelim + '_rels' + PathDelim + 'workbook.xml.rels'; OOXML_PATH_XL_WORKBOOK = 'xl' + PathDelim + 'workbook.xml'; OOXML_PATH_XL_STYLES = 'xl' + PathDelim + 'styles.xml'; OOXML_PATH_XL_STRINGS = 'xl' + PathDelim + 'sharedStrings.xml'; OOXML_PATH_XL_WORKSHEETS = 'xl' + PathDelim + 'worksheets' + PathDelim; { OOXML schemas constants } SCHEMAS_TYPES = 'http://schemas.openxmlformats.org/package/2006/content-types'; SCHEMAS_RELS = 'http://schemas.openxmlformats.org/package/2006/relationships'; SCHEMAS_DOC_RELS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'; SCHEMAS_DOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument'; 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'; SCHEMAS_SPREADML = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'; { OOXML mime types constants } MIME_XML = 'application/xml'; MIME_RELS = 'application/vnd.openxmlformats-package.relationships+xml'; MIME_SPREADML = 'application/vnd.openxmlformats-officedocument.spreadsheetml'; MIME_SHEET = MIME_SPREADML + '.sheet.main+xml'; MIME_WORKSHEET = MIME_SPREADML + '.worksheet+xml'; MIME_STYLES = MIME_SPREADML + '.styles+xml'; MIME_STRINGS = MIME_SPREADML + '.sharedStrings+xml'; { TsSpreadOOXMLWriter } procedure TsSpreadOOXMLWriter.WriteGlobalFiles; begin // WriteCellsToStream(AStream, AData.GetFirstWorksheet.FCells); FContentTypes := XML_HEADER + LineEnding + '' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ''; FRelsRels := XML_HEADER + LineEnding + '' + LineEnding + '' + LineEnding + ''; FStyles := XML_HEADER + LineEnding + '' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ''; end; procedure TsSpreadOOXMLWriter.WriteContent(AData: TsWorkbook); var i: Integer; begin { Workbook relations - Mark relation to all sheets } FWorkbookRels := XML_HEADER + LineEnding + '' + LineEnding + '' + LineEnding + '' + LineEnding; for i := 1 to AData.GetWorksheetCount do begin FWorkbookRels := FWorkbookRels + '' + LineEnding; end; FWorkbookRels := FWorkbookRels + ''; // Global workbook data - Mark all sheets FWorkbook := XML_HEADER + LineEnding + '' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding; for i := 1 to AData.GetWorksheetCount do begin FWorkbook := FWorkbook + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding; end; FWorkbook := FWorkbook + ' ' + LineEnding + ''; // Preparation for Shared strings FSharedStringsCount := 0; FSharedStrings := ''; // Write all worksheets, which fills also FSharedStrings SetLength(FSheets, 0); for i := 0 to AData.GetWorksheetCount - 1 do begin WriteWorksheet(Adata.GetWorksheetByIndex(i)); end; // Finalization of the shared strings document FSharedStrings := XML_HEADER + LineEnding + '' + LineEnding + FSharedStrings + { ' ' + LineEnding + ' First' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' Second' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' Third' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' Fourth' + LineEnding + ' ' + LineEnding + } ''; end; procedure TsSpreadOOXMLWriter.WriteWorksheet(CurSheet: TsWorksheet); var CurStr: Integer; begin CurStr := Length(FSheets); SetLength(FSheets, CurStr + 1); FSheets[CurStr] := XML_HEADER + LineEnding + '' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 1' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 2' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 3' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 4' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 0' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 1' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 2' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' 3' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ' ' + LineEnding + ''; end; destructor TsSpreadOOXMLWriter.Destroy; begin SetLength(FSheets, 0); SetLength(FSSheets, 0); inherited Destroy; end; { Writes a string to a file. Helper convenience method. } procedure TsSpreadOOXMLWriter.WriteStringToFile(AFileName, AString: string); var TheStream : TFileStream; S : String; begin TheStream := TFileStream.Create(AFileName, fmCreate); S:=AString; TheStream.WriteBuffer(Pointer(S)^,Length(S)); TheStream.Free; end; { Writes an OOXML document to the disc } procedure TsSpreadOOXMLWriter.WriteToFile(const AFileName: string; AData: TsWorkbook; const AOverwriteExisting: Boolean); var FZip: TZipper; i: Integer; begin { Fill the strings with the contents of the files } WriteGlobalFiles(); WriteContent(AData); { Write the data to streams } FSContentTypes := TStringStream.Create(FContentTypes); FSRelsRels := TStringStream.Create(FRelsRels); FSWorkbookRels := TStringStream.Create(FWorkbookRels); FSWorkbook := TStringStream.Create(FWorkbook); FSStyles := TStringStream.Create(FStyles); FSSharedStrings := TStringStream.Create(FSharedStrings); SetLength(FSSheets, Length(FSheets)); for i := 0 to Length(FSheets) - 1 do FSSheets[i] := TStringStream.Create(FSheets[i]); { Now compress the files } FZip := TZipper.Create; try FZip.FileName := AFileName; FZip.Entries.AddFileEntry(FSContentTypes, OOXML_PATH_TYPES); FZip.Entries.AddFileEntry(FSRelsRels, OOXML_PATH_RELS_RELS); FZip.Entries.AddFileEntry(FSWorkbookRels, OOXML_PATH_XL_RELS_RELS); FZip.Entries.AddFileEntry(FSWorkbook, OOXML_PATH_XL_WORKBOOK); FZip.Entries.AddFileEntry(FSStyles, OOXML_PATH_XL_STYLES); FZip.Entries.AddFileEntry(FSSharedStrings, OOXML_PATH_XL_STRINGS); for i := 0 to Length(FSheets) - 1 do FZip.Entries.AddFileEntry(FSSheets[i], OOXML_PATH_XL_WORKSHEETS + 'sheet' + IntToStr(i + 1) + '.xml'); FZip.ZipAllFiles; finally FSContentTypes.Free; FSRelsRels.Free; FSWorkbookRels.Free; FSWorkbook.Free; FSStyles.Free; FSSharedStrings.Free; for i := 0 to Length(FSSheets) - 1 do FSSheets[i].Free; FZip.Free; end; end; procedure TsSpreadOOXMLWriter.WriteToStream(AStream: TStream; AData: TsWorkbook); begin // Not supported at the moment raise Exception.Create('TsSpreadOpenDocWriter.WriteToStream not supported'); end; { Writes a string to the sheet } procedure TsSpreadOOXMLWriter.WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string; ACell: PCell); begin end; { Writes a number (64-bit IEE 754 floating point) to the sheet } procedure TsSpreadOOXMLWriter.WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); begin end; { Registers this reader / writer on fpSpreadsheet } initialization RegisterSpreadFormat(TsCustomSpreadReader, TsSpreadOOXMLWriter, sfOOXML); end.