fpspreadsheet: Improved detection of space separators in date/time formats. Generally valid, no modification of xmlread required. Side-effect: leading spaces of xml elements have to be skipped manually

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3122 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-06-01 18:59:29 +00:00
parent 27967f5e66
commit 976e037770

View File

@ -19,9 +19,6 @@ http://docs.oasis-open.org/office/v1.1/OS/OpenDocument-v1.1.pdf
AUTHORS: Felipe Monteiro de Carvalho / Jose Luis Jurado Rincon AUTHORS: Felipe Monteiro de Carvalho / Jose Luis Jurado Rincon
} }
{ TODO: Remove the date/time separator workaround in ReadNumStyle once a patch
giving access to PreserveSpaces in xmlRead is available in fpc }
unit fpsopendocument; unit fpsopendocument;
@ -183,7 +180,7 @@ const
OPENDOC_PATH_SETTINGS = 'settings.xml'; OPENDOC_PATH_SETTINGS = 'settings.xml';
OPENDOC_PATH_STYLES = 'styles.xml'; OPENDOC_PATH_STYLES = 'styles.xml';
OPENDOC_PATH_MIMETYPE = 'mimetype'; OPENDOC_PATH_MIMETYPE = 'mimetype';
OPENDOC_PATH_METAINF = 'META-INF' + '/'; OPENDOC_PATH_METAINF = 'META-INF' + '/';
OPENDOC_PATH_METAINF_MANIFEST = 'META-INF' + '/' + 'manifest.xml'; OPENDOC_PATH_METAINF_MANIFEST = 'META-INF' + '/' + 'manifest.xml';
{ OpenDocument schemas constants } { OpenDocument schemas constants }
@ -635,6 +632,35 @@ var
FileList : TStringList; FileList : TStringList;
BodyNode, SpreadSheetNode, TableNode: TDOMNode; BodyNode, SpreadSheetNode, TableNode: TDOMNode;
StylesNode: TDOMNode; StylesNode: TDOMNode;
{ We have to use our own ReadXMLFile procedure (there is one in xmlread)
because we have to preserve spaces in element text for date/time separator.
As a side-effect we have to skip leading spaces by ourselves. }
procedure ReadXMLFile(out ADoc: TXMLDocument; AFileName: String);
var
parser: TDOMParser;
src: TXMLInputSource;
stream: TStream;
begin
stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyWrite);
try
parser := TDOMParser.Create;
try
parser.Options.PreserveWhiteSpace := true; // This preserves spaces!
src := TXMLInputSource.Create(stream);
try
parser.Parse(src, ADoc);
finally
src.Free;
end;
finally
parser.Free;
end;
finally
stream.Free;
end;
end;
begin begin
//unzip content.xml into AFileName path //unzip content.xml into AFileName path
FilePath := GetTempDir(false); FilePath := GetTempDir(false);
@ -660,6 +686,8 @@ begin
ReadNumFormats(StylesNode); ReadNumFormats(StylesNode);
ReadStyles(StylesNode); ReadStyles(StylesNode);
Doc.Free;
//process the content.xml file //process the content.xml file
ReadXMLFile(Doc, FilePath+'content.xml'); ReadXMLFile(Doc, FilePath+'content.xml');
DeleteFile(FilePath+'content.xml'); DeleteFile(FilePath+'content.xml');
@ -679,6 +707,13 @@ begin
//process each table (sheet) //process each table (sheet)
TableNode := SpreadSheetNode.FindNode('table:table'); TableNode := SpreadSheetNode.FindNode('table:table');
while Assigned(TableNode) do begin while Assigned(TableNode) do begin
// These nodes occur due to leading spaces which are not skipped
// automatically any more due to PreserveWhiteSpace option applied
// to ReadXMLFile
if TableNode.NodeName = '#text' then begin
TableNode := TableNode.NextSibling;
continue;
end;
FWorkSheet := aData.AddWorksheet(GetAttrValue(TableNode,'table:name')); FWorkSheet := aData.AddWorksheet(GetAttrValue(TableNode,'table:name'));
// Collect column styles used // Collect column styles used
ReadColumns(TableNode); ReadColumns(TableNode);
@ -690,7 +725,7 @@ begin
end; //while Assigned(TableNode) end; //while Assigned(TableNode)
finally finally
Doc.Free; if Assigned(Doc) then Doc.Free;
end; end;
end; end;
@ -848,23 +883,25 @@ end;
procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode); procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
var var
NumFormatNode, node: TDOMNode; NumFormatNode, node, childnode: TDOMNode;
decs: Integer; decs: Integer;
fmtName: String; fmtName: String;
grouping: boolean; grouping: boolean;
fmt: String; fmt: String;
numfmt_nodename, nodename: String;
nf: TsNumberFormat; nf: TsNumberFormat;
nex: Integer; nex: Integer;
s, s1, s2: String; s, s1, s2: String;
sep: String; // separator between date/time parts
begin begin
if not Assigned(AStylesNode) then if not Assigned(AStylesNode) then
exit; exit;
NumFormatNode := AStylesNode.FirstChild; NumFormatNode := AStylesNode.FirstChild;
while Assigned(NumFormatNode) do begin while Assigned(NumFormatNode) do begin
numfmt_nodename := NumFormatNode.NodeName;
// Numbers (nfFixed, nfFixedTh, nfExp) // Numbers (nfFixed, nfFixedTh, nfExp)
if NumFormatNode.NodeName = 'number:number-style' then begin if numfmt_nodename = 'number:number-style' then begin
fmtName := GetAttrValue(NumFormatNode, 'style:name'); fmtName := GetAttrValue(NumFormatNode, 'style:name');
node := NumFormatNode.FindNode('number:number'); node := NumFormatNode.FindNode('number:number');
if node <> nil then begin if node <> nil then begin
@ -890,7 +927,7 @@ begin
end; end;
end else end else
// Percentage // Percentage
if NumFormatNode.NodeName = 'number:percentage-style' then begin if numfmt_nodename = 'number:percentage-style' then begin
fmtName := GetAttrValue(NumFormatNode, 'style:name'); fmtName := GetAttrValue(NumFormatNode, 'style:name');
node := NumFormatNode.FindNode('number:number'); node := NumFormatNode.FindNode('number:number');
if node <> nil then begin if node <> nil then begin
@ -901,8 +938,7 @@ begin
end; end;
end else end else
// Date/Time // Date/Time
if (NumFormatNode.NodeName = 'number:date-style') or if (numfmt_nodename = 'number:date-style') or (numfmt_nodename = 'number:time-style')
(NumFormatNode.NodeName = 'number:time-style')
then begin then begin
fmtName := GetAttrValue(NumFormatNode, 'style:name'); fmtName := GetAttrValue(NumFormatNode, 'style:name');
fmt := ''; fmt := '';
@ -962,15 +998,9 @@ begin
fmt := fmt + 'AM/PM' fmt := fmt + 'AM/PM'
else else
if node.NodeName = 'number:text' then begin if node.NodeName = 'number:text' then begin
{ date/time separator workaround: sep is a space by default childnode := node.FirstChild;
and is replaced here by the TextContent separator. It has the if childnode <> nil then
consequence that the equivalent of the format string "yymmdd" will fmt := fmt + childnode.NodeValue;
be decoded as "yy mm dd"!
Remove this once a patch giving access to PreserveSpaces in xmlRead
is included in fpc. }
sep := node.TextContent;
if sep = '' then sep := ' ';
fmt := fmt + sep;
end; end;
node := node.NextSibling; node := node.NextSibling;
end; end;
@ -997,11 +1027,21 @@ var
autoRowHeight: Boolean; autoRowHeight: Boolean;
i: Integer; i: Integer;
lRow: PRow; lRow: PRow;
s: String;
begin begin
rowsRepeated := 0; rowsRepeated := 0;
row := 0; row := 0;
rowNode := ATableNode.FindNode('table:table-row'); rowNode := ATableNode.FindNode('table:table-row');
while Assigned(rowNode) do begin while Assigned(rowNode) do begin
// These nodes occur due to indentation spaces which are not skipped
// automatically any more due to PreserveWhiteSpace option applied
// to ReadXMLFile
if rowNode.NodeName = '#text' then begin
rowNode := rowNode.NextSibling;
Continue;
end;
// Read rowstyle // Read rowstyle
rowStyleName := GetAttrValue(rowNode, 'table:style-name'); rowStyleName := GetAttrValue(rowNode, 'table:style-name');
rowStyleIndex := FindRowStyleByName(rowStyleName); rowStyleIndex := FindRowStyleByName(rowStyleName);
@ -1018,6 +1058,15 @@ begin
//process each cell of the row //process each cell of the row
cellNode := rowNode.FindNode('table:table-cell'); cellNode := rowNode.FindNode('table:table-cell');
while Assigned(cellNode) do begin while Assigned(cellNode) do begin
// These nodes occur due to indentation spaces which are not skipped
// automatically any more due to PreserveWhiteSpace option applied
// to ReadXMLFile
if cellNode.NodeName = '#text' then begin
cellNode := cellNode.NextSibling;
Continue;
end;
// select this cell value's type // select this cell value's type
paramValueType := GetAttrValue(CellNode, 'office:value-type'); paramValueType := GetAttrValue(CellNode, 'office:value-type');
paramFormula := GetAttrValue(CellNode, 'table:formula'); paramFormula := GetAttrValue(CellNode, 'table:formula');