{
fpsopendocument.pas
Writes an OpenDocument 1.0 Spreadsheet document
An OpenDocument document is a compressed ZIP file with the following files inside:
content.xml - Actual contents
meta.xml - Authoring data
settings.xml - User persistent viewing information, such as zoom, cursor position, etc.
styles.xml - Styles, which are the only way to do formatting
mimetype - application/vnd.oasis.opendocument.spreadsheet
META-INF\manifest.xml - Describes the other files in the archive
Specifications obtained from:
http://docs.oasis-open.org/office/v1.1/OS/OpenDocument-v1.1.pdf
AUTHORS: Felipe Monteiro de Carvalho
}
unit fpsopendocument;
{$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
{ TsSpreadOpenDocWriter }
TsSpreadOpenDocWriter = class(TsCustomSpreadWriter)
protected
// Strings with the contents of files
FMeta, FSettings, FStyles, FContent, FMimetype: string;
FMetaInfManifest: string;
// Streams with the contents of files
FSMeta, FSSettings, FSStyles, FSContent, FSMimetype: TStringStream;
FSMetaInfManifest: TStringStream;
// Routines to write those files
procedure WriteGlobalFiles;
procedure WriteContent(AData: TsWorkbook);
procedure WriteWorksheet(CurSheet: TsWorksheet);
public
{ General writing methods }
procedure WriteStringToFile(AString, AFileName: string);
procedure WriteToFile(AFileName: string; AData: TsWorkbook); override;
procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override;
{ Record writing methods }
procedure WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TsFormula); override;
procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string); override;
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double); override;
end;
implementation
const
{ OpenDocument general XML constants }
XML_HEADER = '';
{ OpenDocument Directory structure constants }
OPENDOC_PATH_CONTENT = 'content.xml';
OPENDOC_PATH_META = 'meta.xml';
OPENDOC_PATH_SETTINGS = 'settings.xml';
OPENDOC_PATH_STYLES = 'styles.xml';
OPENDOC_PATH_MIMETYPE = 'mimetype';
OPENDOC_PATH_METAINF = 'META-INF' + PathDelim;
OPENDOC_PATH_METAINF_MANIFEST = 'META-INF' + PathDelim + 'manifest.xml';
{ OpenDocument schemas constants }
SCHEMAS_XMLNS_OFFICE = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0';
SCHEMAS_XMLNS_DCTERMS = 'http://purl.org/dc/terms/';
SCHEMAS_XMLNS_META = 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0';
SCHEMAS_XMLNS = 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties';
SCHEMAS_XMLNS_CONFIG = 'urn:oasis:names:tc:opendocument:xmlns:config:1.0';
SCHEMAS_XMLNS_OOO = 'http://openoffice.org/2004/office';
SCHEMAS_XMLNS_MANIFEST = 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0';
SCHEMAS_XMLNS_FO = 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0';
SCHEMAS_XMLNS_STYLE = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0';
SCHEMAS_XMLNS_SVG = 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0';
SCHEMAS_XMLNS_TABLE = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0';
SCHEMAS_XMLNS_TEXT = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0';
SCHEMAS_XMLNS_V = 'urn:schemas-microsoft-com:vml';
SCHEMAS_XMLNS_NUMBER = 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0';
SCHEMAS_XMLNS_CHART = 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0';
SCHEMAS_XMLNS_DR3D = 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0';
SCHEMAS_XMLNS_MATH = 'http://www.w3.org/1998/Math/MathML';
SCHEMAS_XMLNS_FORM = 'urn:oasis:names:tc:opendocument:xmlns:form:1.0';
SCHEMAS_XMLNS_SCRIPT = 'urn:oasis:names:tc:opendocument:xmlns:script:1.0';
SCHEMAS_XMLNS_OOOW = 'http://openoffice.org/2004/writer';
SCHEMAS_XMLNS_OOOC = 'http://openoffice.org/2004/calc';
SCHEMAS_XMLNS_DOM = 'http://www.w3.org/2001/xml-events';
SCHEMAS_XMLNS_XFORMS = 'http://www.w3.org/2002/xforms';
SCHEMAS_XMLNS_XSD = 'http://www.w3.org/2001/XMLSchema';
SCHEMAS_XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance';
{ TsSpreadOpenDocWriter }
procedure TsSpreadOpenDocWriter.WriteGlobalFiles;
begin
FMimetype := 'application/vnd.oasis.opendocument.spreadsheet';
FMetaInfManifest :=
XML_HEADER + LineEnding +
'' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
'';
FMeta :=
XML_HEADER + LineEnding +
'' + LineEnding +
' ' + LineEnding +
' FPSpreadsheet Library' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
'';
FSettings :=
XML_HEADER + LineEnding +
'' + LineEnding +
'' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' Tabelle1' + LineEnding +
' 100' + LineEnding +
' 100' + LineEnding +
' false' + LineEnding +
' true' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' 3' + LineEnding +
' 2' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + 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 +
'';
end;
procedure TsSpreadOpenDocWriter.WriteContent(AData: TsWorkbook);
var
i: Integer;
begin
FContent :=
XML_HEADER + LineEnding +
'' + LineEnding +
' ' + LineEnding +
// Fonts
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
// Automatic styles
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
' ' + LineEnding +
// Body
' ' + LineEnding +
' ' + LineEnding;
// Write all worksheets
for i := 0 to AData.GetWorksheetCount - 1 do
begin
WriteWorksheet(Adata.GetWorksheetByIndex(i));
end;
FContent := FContent +
' ' + LineEnding +
' ' + LineEnding +
'';
end;
procedure TsSpreadOpenDocWriter.WriteWorksheet(CurSheet: TsWorksheet);
var
j, k: Integer;
CurCell: PCell;
CurRow: array of PCell;
LastColNum: Cardinal;
begin
LastColNum := CurSheet.GetLastColNumber;
// Header
FContent := FContent +
' ' + LineEnding +
' ' + LineEnding;
// The cells need to be written in order, row by row, cell by cell
for j := 0 to CurSheet.GetLastRowNumber do
begin
FContent := FContent +
' ' + LineEnding;
// First make an array with the cells of this row in their respective order
// nil pointers indicate empty cells, so it's necessary to initialize the array
SetLength(CurRow, LastColNum + 1);
for k := 0 to LastColNum do CurRow[k] := nil;
// Now fill the array with the cells in their proper place
for k := 0 to CurSheet.FCells.Count - 1 do
begin
CurCell := CurSheet.FCells.Items[k];
if CurCell^.Row = j then CurRow[CurCell^.Col] := CurCell;
end;
// And now write all cells from this row
for k := 0 to LastColNum do
begin
CurCell := CurRow[k];
if CurCell = nil then
FContent := FContent + '' + LineEnding
else WriteCellCallback(CurCell, nil);
end;
FContent := FContent +
' ' + LineEnding;
end;
// Clean up
SetLength(CurRow, 0);
// Footer
FContent := FContent +
' ' + LineEnding;
end;
{
Writes a string to a file. Helper convenience method.
}
procedure TsSpreadOpenDocWriter.WriteStringToFile(AString, AFileName: 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 TsSpreadOpenDocWriter.WriteToFile(AFileName: string; AData: TsWorkbook);
var
FZip: TZipper;
begin
{ Fill the strings with the contents of the files }
WriteGlobalFiles();
WriteContent(AData);
{ Write the data to streams }
FSMeta := TStringStream.Create(FMeta);
FSSettings := TStringStream.Create(FSettings);
FSStyles := TStringStream.Create(FStyles);
FSContent := TStringStream.Create(FContent);
FSMimetype := TStringStream.Create(FMimetype);
FSMetaInfManifest := TStringStream.Create(FMetaInfManifest);
{ Now compress the files }
FZip := TZipper.Create;
try
FZip.FileName := AFileName;
FZip.Entries.AddFileEntry(FSMeta, OPENDOC_PATH_META);
FZip.Entries.AddFileEntry(FSSettings, OPENDOC_PATH_SETTINGS);
FZip.Entries.AddFileEntry(FSStyles, OPENDOC_PATH_STYLES);
FZip.Entries.AddFileEntry(FSContent, OPENDOC_PATH_CONTENT);
FZip.Entries.AddFileEntry(FSMimetype, OPENDOC_PATH_MIMETYPE);
FZip.Entries.AddFileEntry(FSMetaInfManifest, OPENDOC_PATH_METAINF_MANIFEST);
FZip.ZipAllFiles;
finally
FZip.Free;
FSMeta.Free;
FSSettings.Free;
FSStyles.Free;
FSContent.Free;
FSMimetype.Free;
FSMetaInfManifest.Free;
end;
end;
procedure TsSpreadOpenDocWriter.WriteToStream(AStream: TStream; AData: TsWorkbook);
begin
// Not supported at the moment
raise Exception.Create('TsSpreadOpenDocWriter.WriteToStream not supported');
end;
procedure TsSpreadOpenDocWriter.WriteFormula(AStream: TStream; const ARow,
ACol: Word; const AFormula: TsFormula);
begin
{ // The row should already be the correct one
FContent := FContent +
' ' + LineEnding +
' ' + AFormula.DoubleValue + '' + LineEnding +
' ' + LineEnding;
1833
}
end;
procedure TsSpreadOpenDocWriter.WriteLabel(AStream: TStream; const ARow,
ACol: Word; const AValue: string);
begin
// The row should already be the correct one
FContent := FContent +
' ' + LineEnding +
' ' + AValue + '' + LineEnding +
' ' + LineEnding;
end;
procedure TsSpreadOpenDocWriter.WriteNumber(AStream: TStream; const ARow,
ACol: Cardinal; const AValue: double);
begin
// The row should already be the correct one
FContent := FContent +
' ' + LineEnding +
' ' + FloatToStr(AValue) + '' + LineEnding +
' ' + LineEnding;
end;
{
Registers this reader / writer on fpSpreadsheet
}
initialization
RegisterSpreadFormat(TsCustomSpreadReader, TsSpreadOpenDocWriter, sfOpenDocument);
end.