fpspreadsheet: Fix ods writing incorrect nfTimeInterval format.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3386 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2014-07-27 17:31:02 +00:00
parent c3d3cac3bc
commit 3ef1b5b331
4 changed files with 218 additions and 204 deletions

View File

@ -39,15 +39,18 @@
<Unit0> <Unit0>
<Filename Value="opendocread.lpr"/> <Filename Value="opendocread.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="opendocread"/>
</Unit0> </Unit0>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>
<Version Value="11"/> <Version Value="11"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<Target>
<Filename Value="opendocread"/>
</Target>
<SearchPaths> <SearchPaths>
<OtherUnitFiles Value=".."/> <OtherUnitFiles Value="..\.."/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
<SrcPath Value=".."/> <SrcPath Value=".."/>
</SearchPaths> </SearchPaths>
<Parsing> <Parsing>
@ -55,11 +58,5 @@
<UseAnsiStrings Value="False"/> <UseAnsiStrings Value="False"/>
</SyntaxOptions> </SyntaxOptions>
</Parsing> </Parsing>
<Other>
<CompilerMessages>
<UseMsgFile Value="True"/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions> </CompilerOptions>
</CONFIG> </CONFIG>

View File

@ -1094,7 +1094,8 @@ begin
then begin then begin
Result := true; Result := true;
end else end else
if FSections[section].Elements[elem].Token in [nftYear, nftMonth, nftDay, nftExpChar, nftCurrSymbol] if FSections[section].Elements[elem].Token in
[nftYear, nftMonth, nftDay, nftExpChar, nftCurrSymbol]
then begin then begin
Result := false; Result := false;
exit; exit;

View File

@ -59,14 +59,14 @@ type
{ TsSpreadOpenDocNumFormatParser } { TsSpreadOpenDocNumFormatParser }
TsSpreadOpenDocNumFormatParser = class(TsNumFormatParser) TsSpreadOpenDocNumFormatParser = class(TsNumFormatParser)
private private
function BuildCurrencyXMLAsString(ASection: Integer; AIndent: String): String; function BuildCurrencyXMLAsString(ASection: Integer): String;
function BuildDateTimeXMLAsString(ASection: Integer; AIndent: String; function BuildDateTimeXMLAsString(ASection: Integer;
out AIsTimeOnly: Boolean): String; out AIsTimeOnly, AIsInterval: Boolean): String;
protected protected
function BuildXMLAsStringFromSection(ASection: Integer; function BuildXMLAsStringFromSection(ASection: Integer;
AIndent,AFormatName: String): String; AFormatName: String): String;
public public
function BuildXMLAsString(AIndent,AFormatName: String): String; function BuildXMLAsString(AFormatName: String): String;
end; end;
{ TsSpreadOpenDocReader } { TsSpreadOpenDocReader }
@ -101,7 +101,9 @@ type
function ReadFont(ANode: TDOMnode; IsDefaultFont: Boolean): Integer; function ReadFont(ANode: TDOMnode; IsDefaultFont: Boolean): Integer;
procedure ReadRowsAndCells(ATableNode: TDOMNode); procedure ReadRowsAndCells(ATableNode: TDOMNode);
procedure ReadRowStyle(AStyleNode: TDOMNode); procedure ReadRowStyle(AStyleNode: TDOMNode);
protected protected
FPointSeparatorSettings: TFormatSettings;
procedure CreateNumFormatList; override; procedure CreateNumFormatList; override;
procedure ReadNumFormats(AStylesNode: TDOMNode); procedure ReadNumFormats(AStylesNode: TDOMNode);
procedure ReadSettings(AOfficeSettingsNode: TDOMNode); procedure ReadSettings(AOfficeSettingsNode: TDOMNode);
@ -295,8 +297,41 @@ type
RowStyleIndex: Integer; // index into FRowStyleList of reader RowStyleIndex: Integer; // index into FRowStyleList of reader
DefaultCellStyleIndex: Integer; // Index of default row style in FCellStyleList of reader DefaultCellStyleIndex: Integer; // Index of default row style in FCellStyleList of reader
end; end;
(*
{ Utilities }
function DecodeODSTime(ATimeStr: String; out AHours, AMinutes, ASeconds, AMilliseconds: Word): Boolean;
var
p: Integer;
val: String;
hrStr: String;
minStr: String;
secStr: String;
msecStr: String;
begin
Result := false;
ATimeStr := Uppercase(ATimeStr);
if ATimeStr = '' then
exit;
if ATimeStr[1] <> 'P' then
exit;
if (Length(ATimeStr) < 2) or (ATimeStr[2] <> 'T') then
exit;
p := 3;
val := '';
while p <= Length(ATimeStr) do begin
case ATimeStr[p] of
'0'..'9': val := val + ATimeStr[p];
'H': begin hrStr := val; val := ''; end;
'M': begin minStr := val; val := ''; end;
'S': begin secStr := val; val := ''; end;
',', '.':
end;
end;
end;
*)
{ TsSpreadOpenDocNumFormatList } { TsSpreadOpenDocNumFormatList }
procedure TsSpreadOpenDocNumFormatList.AddBuiltinFormats; procedure TsSpreadOpenDocNumFormatList.AddBuiltinFormats;
@ -315,8 +350,7 @@ end;
{ TsSpreadOpenDocNumFormatParser } { TsSpreadOpenDocNumFormatParser }
function TsSpreadOpenDocNumFormatParser.BuildCurrencyXMLAsString(ASection: Integer; function TsSpreadOpenDocNumFormatParser.BuildCurrencyXMLAsString(ASection: Integer): String;
AIndent: String): String;
var var
el: Integer; el: Integer;
clr: TsColorValue; clr: TsColorValue;
@ -332,47 +366,46 @@ begin
nftColor: nftColor:
begin begin
clr := FWorkbook.GetPaletteColor(Elements[el].IntValue); clr := FWorkbook.GetPaletteColor(Elements[el].IntValue);
Result := Result + AIndent + Result := Result +
' <style:text-properties fo:color="' + ColorToHTMLColorStr(clr) + '" />' + LineEnding; ' <style:text-properties fo:color="' + ColorToHTMLColorStr(clr) + '" />';
inc(el); inc(el);
end; end;
nftSign, nftSignBracket: nftSign, nftSignBracket:
begin begin
Result := Result + AIndent + Result := Result +
' <number:text>' + Elements[el].TextValue + '</number:text>' + LineEnding; ' <number:text>' + Elements[el].TextValue + '</number:text>';
inc(el); inc(el);
end; end;
nftSpace: nftSpace:
begin begin
Result := Result + AIndent + Result := Result +
' <number:text><![CDATA[ ]]></number:text>' + LineEnding; ' <number:text><![CDATA[ ]]></number:text>';
inc(el); inc(el);
end; end;
nftCurrSymbol: nftCurrSymbol:
begin begin
Result := Result + AIndent + Result := Result +
' <number:currency-symbol>' + Elements[el].TextValue + ' <number:currency-symbol>' + Elements[el].TextValue +
'</number:currency-symbol>' + LineEnding; '</number:currency-symbol>';
inc(el); inc(el);
end; end;
nftOptDigit: nftOptDigit:
if IsNumberAt(ASection, el, nf, decs, el) then if IsNumberAt(ASection, el, nf, decs, el) then
Result := Result + AIndent + Result := Result +
' <number:number decimal-places="' + IntToStr(decs) + ' <number:number decimal-places="' + IntToStr(decs) +
'" number:min-integer-digits="1" number:grouping="true" />' '" number:min-integer-digits="1" number:grouping="true" />';
+ LineEnding;
nftDigit: nftDigit:
if IsNumberAt(ASection, el, nf, decs, el) then if IsNumberAt(ASection, el, nf, decs, el) then
Result := Result + AIndent + Result := Result +
' <number:number decimal-places="' + IntToStr(decs) + ' <number:number decimal-places="' + IntToStr(decs) +
'" number:min-integer-digits="1" />' + LineEnding; '" number:min-integer-digits="1" />';
nftRepeat: nftRepeat:
begin begin
if FSections[ASection].Elements[el].TextValue = ' ' then if FSections[ASection].Elements[el].TextValue = ' ' then
s := '<![CDATA[ ]]>' else s := '<![CDATA[ ]]>' else
s := FSections[ASection].Elements[el].TextValue; s := FSections[ASection].Elements[el].TextValue;
Result := Result + AIndent + Result := Result +
' <number:text>' + s + '</number:text>' + LineEnding; ' <number:text>' + s + '</number:text>';
inc(el); inc(el);
end end
else else
@ -382,7 +415,7 @@ begin
end; end;
function TsSpreadOpenDocNumFormatParser.BuildDateTimeXMLAsString(ASection: Integer; function TsSpreadOpenDocNumFormatParser.BuildDateTimeXMLAsString(ASection: Integer;
AIndent: String; out AIsTimeOnly: boolean): String; out AIsTimeOnly, AIsInterval: boolean): String;
var var
el: Integer; el: Integer;
s: String; s: String;
@ -390,6 +423,7 @@ var
begin begin
Result := ''; Result := '';
AIsTimeOnly := true; AIsTimeOnly := true;
AIsInterval := false;
with FSections[ASection] do begin with FSections[ASection] do begin
el := 0; el := 0;
while el < Length(Elements) do begin while el < Length(Elements) do begin
@ -399,8 +433,8 @@ begin
prevToken := Elements[el].Token; prevToken := Elements[el].Token;
AIsTimeOnly := false; AIsTimeOnly := false;
s := IfThen(Elements[el].IntValue > 2, 'number:style="long" ', ''); s := IfThen(Elements[el].IntValue > 2, 'number:style="long" ', '');
Result := Result + AIndent + Result := Result +
' <number:year ' + s + '/>' + LineEnding; '<number:year ' + s + '/>';
end; end;
nftMonth: nftMonth:
@ -413,8 +447,8 @@ begin
3: s := 'number:textual="true" '; 3: s := 'number:textual="true" ';
4: s := 'number:style="long" number:textual="true" '; 4: s := 'number:style="long" number:textual="true" ';
end; end;
Result := result + AIndent + Result := result +
' <number:month ' + s + '/>' + LineEnding; '<number:month ' + s + '/>';
end; end;
nftDay: nftDay:
@ -427,8 +461,8 @@ begin
3: s := 'day-of-week '; 3: s := 'day-of-week ';
4: s := 'day-of-week number:style="long" '; 4: s := 'day-of-week number:style="long" ';
end; end;
Result := Result + AIndent + Result := Result +
' <number:' + s + '/>' + LineEnding; '<number:' + s + '/>';
end; end;
nftHour, nftMinute, nftSecond: nftHour, nftMinute, nftSecond:
@ -441,9 +475,9 @@ begin
end; end;
s := s + IfThen(abs(Elements[el].IntValue) = 1, '', 'number:style="long" '); s := s + IfThen(abs(Elements[el].IntValue) = 1, '', 'number:style="long" ');
if Elements[el].IntValue < 0 then if Elements[el].IntValue < 0 then
s := s + 'number:truncate-on-overflow="false" '; AIsInterval := true;
Result := Result + AIndent + Result := Result +
' <number:' + s + '/>' + LineEnding; '<number:' + s + '/>';
end; end;
nftMilliseconds: nftMilliseconds:
@ -464,21 +498,20 @@ begin
s := FWorkbook.FormatSettings.TimeSeparator; s := FWorkbook.FormatSettings.TimeSeparator;
end; end;
end; end;
Result := Result + AIndent + Result := Result +
' <number:text>' + s + '</number:text>' + LineEnding; '<number:text>' + s + '</number:text>';
end; end;
nftAMPM: nftAMPM:
Result := Result + AIndent + Result := Result +
' <number:am-pm />' + LineEnding; '<number:am-pm />';
end; end;
inc(el); inc(el);
end; end;
end; end;
end; end;
function TsSpreadOpenDocNumFormatParser.BuildXMLAsString(AIndent, function TsSpreadOpenDocNumFormatParser.BuildXMLAsString(AFormatName: String): String;
AFormatName: String): String;
var var
i: Integer; i: Integer;
begin begin
@ -488,11 +521,11 @@ begin
positive section (index 0), then the negative section (index 1), and positive section (index 0), then the negative section (index 1), and
finally the zero section (index 2) which contains the style-map. } finally the zero section (index 2) which contains the style-map. }
for i:=0 to Length(FSections)-1 do for i:=0 to Length(FSections)-1 do
Result := Result + BuildXMLAsStringFromSection(i, AIndent, AFormatName); Result := Result + BuildXMLAsStringFromSection(i, AFormatName);
end; end;
function TsSpreadOpenDocNumFormatParser.BuildXMLAsStringFromSection( function TsSpreadOpenDocNumFormatParser.BuildXMLAsStringFromSection(
ASection: Integer; AIndent,AFormatName: String): String; ASection: Integer; AFormatName: String): String;
var var
nf : TsNumberFormat; nf : TsNumberFormat;
decs: Byte; decs: Byte;
@ -506,6 +539,7 @@ var
el: Integer; el: Integer;
s: String; s: String;
isTimeOnly: Boolean; isTimeOnly: Boolean;
isInterval: Boolean;
begin begin
Result := ''; Result := '';
@ -521,17 +555,17 @@ begin
// The file corresponding to the last section contains the styleMap. // The file corresponding to the last section contains the styleMap.
if (ASection = ns - 1) then if (ASection = ns - 1) then
case ns of case ns of
2: sStyleMap := AIndent + 2: sStyleMap :=
'<style:map ' + '<style:map ' +
'style:apply-style-name="' + AFormatName + 'P0" ' + 'style:apply-style-name="' + AFormatName + 'P0" ' +
'style:condition="value()&gt;=0" />' + LineEnding; // >= 0 'style:condition="value()&gt;=0" />'; // >= 0
3: sStyleMap := AIndent + 3: sStyleMap :=
'<style:map '+ '<style:map '+
'style:apply-style-name="' + AFormatName + 'P0" ' + // > 0 'style:apply-style-name="' + AFormatName + 'P0" ' + // > 0
'style:condition="value()&gt;0" />' + LineEnding + AIndent + 'style:condition="value()&gt;0" />' +
'<style:map '+ '<style:map '+
'style:apply-style-name="' + AFormatName + 'P1" ' + // < 0 'style:apply-style-name="' + AFormatName + 'P1" ' + // < 0
'style:condition="value()&lt;0" />' + LineEnding; 'style:condition="value()&lt;0" />';
else else
raise Exception.Create('At most 3 format sections allowed.'); raise Exception.Create('At most 3 format sections allowed.');
end end
@ -543,7 +577,7 @@ begin
next := 0; next := 0;
if IsTokenAt(nftColor, ASection, 0) then begin if IsTokenAt(nftColor, ASection, 0) then begin
clr := FWorkbook.GetPaletteColor(Elements[0].IntValue); clr := FWorkbook.GetPaletteColor(Elements[0].IntValue);
sColor := AIndent + '<style:text-properties fo:color="' + ColorToHTMLColorStr(clr) + '" />' + LineEnding; sColor := '<style:text-properties fo:color="' + ColorToHTMLColorStr(clr) + '" />' + LineEnding;
next := 1; next := 1;
end; end;
if IsNumberAt(ASection, next, nf, decs, next) then begin if IsNumberAt(ASection, next, nf, decs, next) then begin
@ -552,30 +586,30 @@ begin
// nfFixed, nfFixedTh // nfFixed, nfFixedTh
if (next = Length(Elements)) then begin if (next = Length(Elements)) then begin
Result := AIndent + Result :=
'<number:number-style style:name="' + AFormatName + '">' + LineEnding + '<number:number-style style:name="' + AFormatName + '">' +
sColor + AIndent + sColor +
'<number:number ' + '<number:number ' +
'number:min-integer-digits="1" ' + sGrouping + 'number:min-integer-digits="1" ' + sGrouping +
'number:decimal-places="' + IntToStr(decs) + 'number:decimal-places="' + IntToStr(decs) +
'" />' + LineEnding + '" />' +
sStylemap + AIndent + sStylemap +
'</number:number-style>' + LineEnding; '</number:number-style>';
exit; exit;
end; end;
// nfPercentage // nfPercentage
if IsTokenAt(nftPercent, ASection, next) and (next+1 = Length(Elements)) if IsTokenAt(nftPercent, ASection, next) and (next+1 = Length(Elements))
then begin then begin
Result := AIndent + Result :=
'<number:percentage-style style:name="' + AFormatName + '">' + LineEnding + '<number:percentage-style style:name="' + AFormatName + '">' +
sColor + AIndent + sColor +
'<number:number ' + '<number:number ' +
'number:min-integer-digits="1" ' + sGrouping + 'number:min-integer-digits="1" ' + sGrouping +
'number:decimal-places="' + IntToStr(decs) + '" />' + LineEnding + AIndent + 'number:decimal-places="' + IntToStr(decs) + '" />' +
' <number:text>%</number:text>' + LineEnding + '<number:text>%</number:text>' +
sStyleMap + AIndent + sStyleMap +
'</number:percentage-style>' + LineEnding; '</number:percentage-style>';
exit; exit;
end; end;
@ -593,13 +627,13 @@ begin
expdig := Elements[next+1].IntValue expdig := Elements[next+1].IntValue
else else
exit; exit;
Result := AIndent + Result :=
'<number:number-style style:name="' + AFormatName + '">' + LineEnding + '<number:number-style style:name="' + AFormatName + '">' +
sColor + AIndent + sColor +
'<number:scientific-number number:decimal-places="' + IntToStr(decs) +'" '+ '<number:scientific-number number:decimal-places="' + IntToStr(decs) +'" '+
'number:min-integer-digits="1" '+ 'number:min-integer-digits="1" '+
'number:min-exponent-digits="' + IntToStr(expdig) +'" />' + 'number:min-exponent-digits="' + IntToStr(expdig) +'" />' +
sStylemap + AIndent + sStylemap +
'</number:number-style>'; '</number:number-style>';
exit; exit;
end; end;
@ -620,13 +654,13 @@ begin
while el < Length(Elements) do begin while el < Length(Elements) do begin
if Elements[el].Token = nftExpDigits then begin if Elements[el].Token = nftExpDigits then begin
expdig := Elements[el].IntValue; expdig := Elements[el].IntValue;
Result := AIndent + Result :=
'<number:number-style style:name="' + AFormatName + '">' + LineEnding + '<number:number-style style:name="' + AFormatName + '">' +
sColor + AIndent + sColor +
'<number:scientific-number number:decimal-places="' + IntToStr(decs) +'" '+ '<number:scientific-number number:decimal-places="' + IntToStr(decs) +'" '+
'number:min-integer-digits="1" '+ 'number:min-integer-digits="1" '+
'number:min-exponent-digits="' + IntToStr(expdig) +'" />' + 'number:min-exponent-digits="' + IntToStr(expdig) +'" />' +
sStylemap + AIndent + sStylemap +
'</number:number-style>'; '</number:number-style>';
exit; exit;
end; end;
@ -638,28 +672,31 @@ begin
// Currency // Currency
nftCurrSymbol: nftCurrSymbol:
begin begin
Result := AIndent + Result :=
'<number:currency-style style:name="' + AFormatName + '">' + LineEnding + '<number:currency-style style:name="' + AFormatName + '">' +
BuildCurrencyXMLAsString(ASection, AIndent) + BuildCurrencyXMLAsString(ASection) +
sStyleMap + LineEnding + sStyleMap +
'</number:currency-style>' + LineEnding; '</number:currency-style>';
exit; exit;
end; end;
// date/time // date/time
nftYear, nftMonth, nftDay, nftHour, nftMinute, nftSecond: nftYear, nftMonth, nftDay, nftHour, nftMinute, nftSecond:
begin begin
s := BuildDateTimeXMLAsString(ASection, AIndent, isTimeOnly); s := BuildDateTimeXMLAsString(ASection, isTimeOnly, isInterval);
if isTimeOnly then if isTimeOnly then begin
Result := Result + AIndent + Result := Result +
'<number:time-style style:name="' + AFormatName + '">' + LineEnding + '<number:time-style style:name="' + AFormatName + '"';
s + AIndent + if isInterval then
'</number:time-style>' + LineEnding Result := Result + ' number:truncate-on-overflow="false"';
else Result := Result + '>' +
Result := Result + AIndent + s +
'<number:date-style style:name="' + AFormatName + '">' + LineEnding + '</number:time-style>';
s + AIndent + end else
'</number:date-style>' + LineEnding; Result := Result +
'<number:date-style style:name="' + AFormatName + '">' +
s +
'</number:date-style>';
exit; exit;
end; end;
end; end;
@ -674,6 +711,9 @@ end;
constructor TsSpreadOpenDocReader.Create(AWorkbook: TsWorkbook); constructor TsSpreadOpenDocReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.';
FCellStyleList := TFPList.Create; FCellStyleList := TFPList.Create;
FColumnStyleList := TFPList.Create; FColumnStyleList := TFPList.Create;
FColumnList := TFPList.Create; FColumnList := TFPList.Create;
@ -852,7 +892,8 @@ var
Value: String; Value: String;
Fmt : TFormatSettings; Fmt : TFormatSettings;
FoundPos : integer; FoundPos : integer;
Hours, Minutes, Seconds, Days: integer; Hours, Minutes, Days: integer;
Seconds: Double;
HoursPos, MinutesPos, SecondsPos: integer; HoursPos, MinutesPos, SecondsPos: integer;
begin begin
Unused(AFormatStr); Unused(AFormatStr);
@ -908,7 +949,7 @@ begin
// Get seconds // Get seconds
SecondsPos := Pos('S', Value); SecondsPos := Pos('S', Value);
if (SecondsPos > 0) and (SecondsPos > MinutesPos) then if (SecondsPos > 0) and (SecondsPos > MinutesPos) then
Seconds := StrToInt(Copy(Value, MinutesPos+1, SecondsPos-MinutesPos-1)) Seconds := StrToFloat(Copy(Value, MinutesPos+1, SecondsPos-MinutesPos-1), FPointSeparatorSettings)
else else
Seconds := 0; Seconds := 0;
@ -1240,13 +1281,10 @@ var
formula: String; formula: String;
stylename: String; stylename: String;
floatValue: Double; floatValue: Double;
fs: TFormatSettings;
valueType: String; valueType: String;
valueStr: String; valueStr: String;
node: TDOMNode; node: TDOMNode;
begin begin
fs := DefaultFormatSettings;
fs.DecimalSeparator := '.';
// Create cell and apply format // Create cell and apply format
if FIsVirtualMode then begin if FIsVirtualMode then begin
@ -1271,7 +1309,7 @@ begin
if UpperCase(valueStr) = '1.#INF' then if UpperCase(valueStr) = '1.#INF' then
FWorksheet.WriteNumber(cell, 1.0/0.0) FWorksheet.WriteNumber(cell, 1.0/0.0)
else begin else begin
floatValue := StrToFloat(valueStr, fs); floatValue := StrToFloat(valueStr, FPointSeparatorSettings);
FWorksheet.WriteNumber(cell, floatValue); FWorksheet.WriteNumber(cell, floatValue);
end; end;
if IsDateTimeFormat(cell^.NumberFormat) then begin if IsDateTimeFormat(cell^.NumberFormat) then begin
@ -1346,15 +1384,11 @@ end;
procedure TsSpreadOpenDocReader.ReadNumber(ARow: Word; ACol : Word; ACellNode : TDOMNode); procedure TsSpreadOpenDocReader.ReadNumber(ARow: Word; ACol : Word; ACellNode : TDOMNode);
var var
FSettings: TFormatSettings;
Value, Str: String; Value, Str: String;
lNumber: Double; lNumber: Double;
styleName: String; styleName: String;
cell: PCell; cell: PCell;
begin begin
FSettings := DefaultFormatSettings;
FSettings.DecimalSeparator:='.';
if FIsVirtualMode then begin if FIsVirtualMode then begin
InitCell(ARow, ACol, FVirtualCell); InitCell(ARow, ACol, FVirtualCell);
cell := @FVirtualCell; cell := @FVirtualCell;
@ -1368,7 +1402,7 @@ begin
begin begin
// Don't merge, or else we can't debug // Don't merge, or else we can't debug
Str := GetAttrValue(ACellNode,'office:value'); Str := GetAttrValue(ACellNode,'office:value');
lNumber := StrToFloat(Str,FSettings); lNumber := StrToFloat(Str, FPointSeparatorSettings);
FWorkSheet.WriteNumber(cell, lNumber); FWorkSheet.WriteNumber(cell, lNumber);
end; end;
@ -1974,7 +2008,6 @@ end;
procedure TsSpreadOpenDocReader.ReadStyles(AStylesNode: TDOMNode); procedure TsSpreadOpenDocReader.ReadStyles(AStylesNode: TDOMNode);
var var
fs: TFormatSettings;
style: TCellStyleData; style: TCellStyleData;
styleNode: TDOMNode; styleNode: TDOMNode;
styleChildNode: TDOMNode; styleChildNode: TDOMNode;
@ -2023,17 +2056,17 @@ var
end; end;
p := pos('pt', s); p := pos('pt', s);
if p = Length(s)-1 then begin if p = Length(s)-1 then begin
wid := StrToFloat(copy(s, 1, p-1), fs); wid := StrToFloat(copy(s, 1, p-1), FPointSeparatorSettings);
continue; continue;
end; end;
p := pos('mm', s); p := pos('mm', s);
if p = Length(s)-1 then begin if p = Length(s)-1 then begin
wid := mmToPts(StrToFloat(copy(s, 1, p-1), fs)); wid := mmToPts(StrToFloat(copy(s, 1, p-1), FPointSeparatorSettings));
Continue; Continue;
end; end;
p := pos('cm', s); p := pos('cm', s);
if p = Length(s)-1 then begin if p = Length(s)-1 then begin
wid := cmToPts(StrToFloat(copy(s, 1, p-1), fs)); wid := cmToPts(StrToFloat(copy(s, 1, p-1), FPointSeparatorSettings));
Continue; Continue;
end; end;
rgb := HTMLColorStrToColor(s); rgb := HTMLColorStrToColor(s);
@ -2065,9 +2098,6 @@ begin
if not Assigned(AStylesNode) then if not Assigned(AStylesNode) then
exit; exit;
fs := DefaultFormatSettings;
fs.DecimalSeparator := '.';
numFmtIndexDefault := NumFormatList.FindByName('N0'); numFmtIndexDefault := NumFormatList.FindByName('N0');
styleNode := AStylesNode.FirstChild; styleNode := AStylesNode.FirstChild;
@ -2463,41 +2493,26 @@ begin
'" xmlns:config="' + SCHEMAS_XMLNS_CONFIG + '" xmlns:config="' + SCHEMAS_XMLNS_CONFIG +
'" xmlns:ooo="' + SCHEMAS_XMLNS_OOO + '">'); '" xmlns:ooo="' + SCHEMAS_XMLNS_OOO + '">');
AppendToStream(FSSettings, AppendToStream(FSSettings,
'<office:settings>'); '<office:settings>' +
AppendToStream(FSSettings, '<config:config-item-set config:name="ooo:view-settings">' +
'<config:config-item-set config:name="ooo:view-settings">'); '<config:config-item-map-indexed config:name="Views">' +
AppendToStream(FSSettings, '<config:config-item-map-entry>' +
'<config:config-item-map-indexed config:name="Views">'); '<config:config-item config:name="ActiveTable" config:type="string">Tabelle1</config:config-item>' +
AppendToStream(FSSettings, '<config:config-item config:name="ZoomValue" config:type="int">100</config:config-item>' +
'<config:config-item-map-entry>'); '<config:config-item config:name="PageViewZoomValue" config:type="int">100</config:config-item>' +
AppendToStream(FSSettings, '<config:config-item config:name="ShowPageBreakPreview" config:type="boolean">false</config:config-item>' +
'<config:config-item config:name="ActiveTable" config:type="string">Tabelle1</config:config-item>'); '<config:config-item config:name="ShowGrid" config:type="boolean">'+FALSE_TRUE[showGrid]+'</config:config-item>' +
AppendToStream(FSSettings, '<config:config-item config:name="HasColumnRowHeaders" config:type="boolean">'+FALSE_TRUE[showHeaders]+'</config:config-item>' +
'<config:config-item config:name="ZoomValue" config:type="int">100</config:config-item>');
AppendToStream(FSSettings,
'<config:config-item config:name="PageViewZoomValue" config:type="int">100</config:config-item>');
AppendToStream(FSSettings,
'<config:config-item config:name="ShowPageBreakPreview" config:type="boolean">false</config:config-item>');
AppendToStream(FSSettings,
'<config:config-item config:name="ShowGrid" config:type="boolean">'+FALSE_TRUE[showGrid]+'</config:config-item>');
AppendToStream(FSSettings,
'<config:config-item config:name="HasColumnRowHeaders" config:type="boolean">'+FALSE_TRUE[showHeaders]+'</config:config-item>');
AppendToStream(FSSettings,
'<config:config-item-map-named config:name="Tables">'); '<config:config-item-map-named config:name="Tables">');
WriteTableSettings(FSSettings); WriteTableSettings(FSSettings);
AppendToStream(FSSettings, AppendToStream(FSSettings,
'</config:config-item-map-named>'); '</config:config-item-map-named>' +
AppendToStream(FSSettings, '</config:config-item-map-entry>' +
' </config:config-item-map-entry>'); '</config:config-item-map-indexed>' +
AppendToStream(FSSettings, '</config:config-item-set>' +
' </config:config-item-map-indexed>'); '</office:settings>' +
AppendToStream(FSSettings,
' </config:config-item-set>');
AppendToStream(FSSettings,
' </office:settings>');
AppendToStream(FSSettings,
'</office:document-settings>'); '</office:document-settings>');
end; end;
@ -2531,39 +2546,27 @@ begin
'</office:styles>'); '</office:styles>');
AppendToStream(FSStyles, AppendToStream(FSStyles,
'<office:automatic-styles>'); '<office:automatic-styles>' +
AppendToStream(FSStyles, '<style:page-layout style:name="pm1">' +
'<style:page-layout style:name="pm1">'); '<style:page-layout-properties fo:margin-top="1.25cm" fo:margin-bottom="1.25cm" fo:margin-left="1.905cm" fo:margin-right="1.905cm" />' +
AppendToStream(FSStyles, '<style:header-style>' +
'<style:page-layout-properties fo:margin-top="1.25cm" fo:margin-bottom="1.25cm" fo:margin-left="1.905cm" fo:margin-right="1.905cm" />'); '<style:header-footer-properties fo:min-height="0.751cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.25cm" fo:margin-top="0cm" />' +
AppendToStream(FSStyles, '</style:header-style>' +
'<style:header-style>', '<style:footer-style>' +
'<style:header-footer-properties fo:min-height="0.751cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.25cm" fo:margin-top="0cm" />', '<style:header-footer-properties fo:min-height="0.751cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.25cm" fo:margin-bottom="0cm" />' +
'</style:header-style>'); '</style:footer-style>' +
AppendToStream(FSStyles, '</style:page-layout>' +
'<style:footer-style>',
'<style:header-footer-properties fo:min-height="0.751cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.25cm" fo:margin-bottom="0cm" />',
'</style:footer-style>');
AppendToStream(FSStyles,
'</style:page-layout>');
AppendToStream(FSStyles,
'</office:automatic-styles>'); '</office:automatic-styles>');
AppendToStream(FSStyles, AppendToStream(FSStyles,
'<office:master-styles>'); '<office:master-styles>' +
AppendToStream(FSStyles, '<style:master-page style:name="Default" style:page-layout-name="pm1">' +
'<style:master-page style:name="Default" style:page-layout-name="pm1">'); '<style:header />' +
AppendToStream(FSStyles, '<style:header-left style:display="false" />' +
'<style:header />', '<style:footer />' +
'<style:header-left style:display="false" />'); '<style:footer-left style:display="false" />' +
AppendToStream(FSStyles, '</style:master-page>' +
'<style:footer />', '</office:master-styles>' +
'<style:footer-left style:display="false" />');
AppendToStream(FSStyles,
'</style:master-page>');
AppendToStream(FSStyles,
'</office:master-styles>');
AppendToStream(FSStyles,
'</office:document-styles>'); '</office:document-styles>');
end; end;
@ -2612,8 +2615,8 @@ begin
WriteRowStyles(FSContent); WriteRowStyles(FSContent);
AppendToStream(FSContent, AppendToStream(FSContent,
'<style:style style:name="ta1" style:family="table" style:master-page-name="Default">', '<style:style style:name="ta1" style:family="table" style:master-page-name="Default">' +
'<style:table-properties table:display="true" style:writing-mode="lr-tb"/>', '<style:table-properties table:display="true" style:writing-mode="lr-tb"/>' +
'</style:style>'); '</style:style>');
// Automatically generated styles // Automatically generated styles
WriteCellStyles(FSContent); WriteCellStyles(FSContent);
@ -2622,7 +2625,7 @@ begin
// Body // Body
AppendToStream(FSContent, AppendToStream(FSContent,
'<office:body>', '<office:body>' +
'<office:spreadsheet>'); '<office:spreadsheet>');
// Write all worksheets // Write all worksheets
@ -2630,8 +2633,8 @@ begin
WriteWorksheet(FSContent, Workbook.GetWorksheetByIndex(i)); WriteWorksheet(FSContent, Workbook.GetWorksheetByIndex(i));
AppendToStream(FSContent, AppendToStream(FSContent,
'</office:spreadsheet>', '</office:spreadsheet>' +
'</office:body>', '</office:body>' +
'</office:document-content>' '</office:document-content>'
); );
@ -2901,7 +2904,7 @@ begin
parser := TsSpreadOpenDocNumFormatParser.Create(Workbook, fmtItem.FormatString, parser := TsSpreadOpenDocNumFormatParser.Create(Workbook, fmtItem.FormatString,
fmtItem.NumFormat); fmtItem.NumFormat);
try try
numFmtXML := parser.BuildXMLAsString(' ', fmtItem.Name); numFmtXML := parser.BuildXMLAsString(fmtItem.Name);
if numFmtXML <> '' then if numFmtXML <> '' then
AppendToStream(AStream, numFmtXML); AppendToStream(AStream, numFmtXML);
finally finally
@ -3541,6 +3544,7 @@ var
displayStr: String; displayStr: String;
lIndex: Integer; lIndex: Integer;
isTimeOnly: Boolean; isTimeOnly: Boolean;
lcfmt: String;
begin begin
Unused(AStream, ACell); Unused(AStream, ACell);
Unused(ARow, ACol); Unused(ARow, ACol);
@ -3553,16 +3557,26 @@ begin
// The row should already be the correct one // The row should already be the correct one
// nfTimeInterval is a special case - let's handle it first:
if (ACell^.NumberFormat = nfTimeInterval) then begin
lcfmt := Lowercase(Copy(ACell^.NumberFormatStr, 1, 2));
strValue := FormatDateTime(ISO8601FormatHoursOverflow, AValue, [fdoInterval]);
displayStr := FormatDateTime(ACell^.NumberFormatStr, AValue, [fdoInterval]);
AppendToStream(AStream, Format(
'<table:table-cell office:value-type="time" office:time-value="%s" %s>' +
'<text:p>%s</text:p>' +
'</table:table-cell>', [strValue, lStyle, displayStr]));
end else begin
// We have to distinguish between time-only values and values that contain date parts. // We have to distinguish between time-only values and values that contain date parts.
isTimeOnly := IsTimeFormat(ACell^.NumberFormat) or IsTimeFormat(ACell^.NumberFormatStr); isTimeOnly := IsTimeFormat(ACell^.NumberFormat) or IsTimeFormat(ACell^.NumberFormatStr);
strValue := FormatDateTime(FMT[isTimeOnly], AValue); strValue := FormatDateTime(FMT[isTimeOnly], AValue);
displayStr := FormatDateTime(ACell^.NumberFormatStr, AValue); displayStr := FormatDateTime(ACell^.NumberFormatStr, AValue);
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<table:table-cell office:value-type="%s" office:%s-value="%s" %s>' + '<table:table-cell office:value-type="%s" office:%s-value="%s" %s>' +
'<text:p>%s</text:p> ' + '<text:p>%s</text:p> ' +
'</table:table-cell>', [DT[isTimeOnly], DT[isTimeOnly], strValue, lStyle, displayStr])); '</table:table-cell>', [DT[isTimeOnly], DT[isTimeOnly], strValue, lStyle, displayStr]));
end; end;
end;
{ {
Registers this reader / writer on fpSpreadsheet Registers this reader / writer on fpSpreadsheet

View File

@ -42,6 +42,8 @@ const
ISO8601FormatExtended='yyyy"-"mm"-"dd"T"hh":"mm":"ss'; ISO8601FormatExtended='yyyy"-"mm"-"dd"T"hh":"mm":"ss';
{@@ ISO 8601 time-only format, used in ODF/opendocument } {@@ ISO 8601 time-only format, used in ODF/opendocument }
ISO8601FormatTimeOnly='"PT"hh"H"nn"M"ss"S"'; ISO8601FormatTimeOnly='"PT"hh"H"nn"M"ss"S"';
{@@ ISO 8601 time-only format, with hours overflow }
ISO8601FormatHoursOverflow='"PT"[hh]"H"nn"M"ss.zz"S"';
// Endianess helper functions // Endianess helper functions
function WordToLE(AValue: Word): Word; function WordToLE(AValue: Word): Word;