fpspreadsheet: Fix localized AM/PM string appearing in time format strings (https://forum.lazarus.freepascal.org/index.php/topic,46069.msg327309.html#msg327309).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7043 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2019-07-16 22:11:00 +00:00
parent b472802603
commit 347c7328d1
5 changed files with 50 additions and 33 deletions

View File

@ -161,12 +161,14 @@ type
by the number format parser from a format string. } by the number format parser from a format string. }
TsNumFormatParams = class(TObject) TsNumFormatParams = class(TObject)
private private
FAllowLocalizedAMPM: Boolean;
protected protected
function GetNumFormat: TsNumberFormat; virtual; function GetNumFormat: TsNumberFormat; virtual;
function GetNumFormatStr: String; virtual; function GetNumFormatStr: String; virtual;
public public
{@@ Array of the format sections } {@@ Array of the format sections }
Sections: TsNumFormatSections; Sections: TsNumFormatSections;
constructor Create;
procedure DeleteElement(ASectionIndex, AElementIndex: Integer); procedure DeleteElement(ASectionIndex, AElementIndex: Integer);
procedure InsertElement(ASectionIndex, AElementIndex: Integer; procedure InsertElement(ASectionIndex, AElementIndex: Integer;
AToken: TsNumFormatToken); AToken: TsNumFormatToken);
@ -175,6 +177,7 @@ type
procedure SetDecimals(AValue: Byte); procedure SetDecimals(AValue: Byte);
procedure SetNegativeRed(AEnable: Boolean); procedure SetNegativeRed(AEnable: Boolean);
procedure SetThousandSep(AEnable: Boolean); procedure SetThousandSep(AEnable: Boolean);
property AllowLocalizedAMPM: boolean read FAllowLocalizedAMPM write FAllowLocalizedAMPM;
property NumFormat: TsNumberFormat read GetNumFormat; property NumFormat: TsNumberFormat read GetNumFormat;
property NumFormatStr: String read GetNumFormatStr; property NumFormatStr: String read GetNumFormatStr;
end; end;
@ -315,7 +318,8 @@ function BuildNumberFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1; const AFormatSettings: TFormatSettings; ADecimals: Integer = -1;
AMinIntDigits: Integer = 1): String; AMinIntDigits: Integer = 1): String;
function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; function BuildFormatStringFromSection(const ASection: TsNumFormatSection;
AllowLocalizedAMPM: Boolean = true): String;
function ApplyTextFormat(AText: String; AParams: TsNumFormatParams): String; function ApplyTextFormat(AText: String; AParams: TsNumFormatParams): String;
function ConvertFloatToStr(AValue: Double; AParams: TsNumFormatParams; function ConvertFloatToStr(AValue: Double; AParams: TsNumFormatParams;
@ -1118,9 +1122,7 @@ end;
{==============================================================================} {==============================================================================}
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Adds an AM/PM format code to a pre-built time formatting string. The strings Adds an AM/PM format code to a pre-built time formatting string.
replacing "AM" or "PM" in the final formatted number are taken from the
TimeAMString or TimePMString of the specified FormatSettings.
@param ATimeFormatString String of time formatting codes (such as 'hh:nn') @param ATimeFormatString String of time formatting codes (such as 'hh:nn')
@param AFormatSettings FormatSettings for locale-dependent information @param AFormatSettings FormatSettings for locale-dependent information
@ -1130,13 +1132,8 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function AddAMPM(const ATimeFormatString: String; function AddAMPM(const ATimeFormatString: String;
const AFormatSettings: TFormatSettings): String; const AFormatSettings: TFormatSettings): String;
var
am, pm: String;
fs: TFormatSettings absolute AFormatSettings;
begin begin
am := IfThen(fs.TimeAMString <> '', fs.TimeAMString, 'AM'); Result := Format('%s AM/PM', [StripAMPM(ATimeFormatString)]);
pm := IfThen(fs.TimePMString <> '', fs.TimePMString, 'PM');
Result := Format('%s %s/%s', [StripAMPM(ATimeFormatString), am, pm]);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -1470,9 +1467,15 @@ end;
@param ASection Parsed section of number format elements as created by the @param ASection Parsed section of number format elements as created by the
number format parser number format parser
@param AllowLocalizedAMPM Replaces "AMPM" in a time format string by "AM/PM".
"AMPM" is allowed by FPS, but not by Excel. When converting a time to
string it is replaced by the localized strings
FormatSettings.TimeAMString/.TimePMString.
@return Excel-compatible format string @return Excel-compatible format string
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; function BuildFormatStringFromSection(const ASection: TsNumFormatSection;
AllowLocalizedAMPM: Boolean = true): String;
var var
element: TsNumFormatElement; element: TsNumFormatElement;
i, n: Integer; i, n: Integer;
@ -1542,7 +1545,12 @@ begin
else Result := Result + DupeString('s', element.IntValue); else Result := Result + DupeString('s', element.IntValue);
nftMilliseconds: nftMilliseconds:
Result := Result + DupeString('0', element.IntValue); Result := Result + DupeString('0', element.IntValue);
nftSign, nftSignBracket, nftExpChar, nftExpSign, nftAMPM, nftDateTimeSep: nftAMPM:
if Lowercase(element.TextValue) = 'ampm' then
Result := Result + 'AM/PM'
else if element.TextValue <> '' then
Result := Result + element.TextValue;
nftSign, nftSignBracket, nftExpChar, nftExpSign, nftDateTimeSep:
if element.TextValue <> '' then Result := Result + element.TextValue; if element.TextValue <> '' then Result := Result + element.TextValue;
nftCurrSymbol: nftCurrSymbol:
if element.TextValue <> '' then if element.TextValue <> '' then
@ -2189,6 +2197,12 @@ end;
{ TsNumFormatParams } { TsNumFormatParams }
{==============================================================================} {==============================================================================}
constructor TsNumFormatParams.Create;
begin
inherited;
FAllowLocalizedAMPM := true;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Deletes a parsed number format element from the specified format section. Deletes a parsed number format element from the specified format section.
@ -2249,7 +2263,7 @@ begin
if Length(Sections) > 0 then begin if Length(Sections) > 0 then begin
Result := BuildFormatStringFromSection(Sections[0]); Result := BuildFormatStringFromSection(Sections[0]);
for i := 1 to High(Sections) do for i := 1 to High(Sections) do
Result := Result + ';' + BuildFormatStringFromSection(Sections[i]); Result := Result + ';' + BuildFormatStringFromSection(Sections[i], FAllowLocalizedAMPM);
end else end else
Result := ''; Result := '';
end; end;

View File

@ -152,8 +152,6 @@ type
procedure AddBuiltinNumFormats; virtual; procedure AddBuiltinNumFormats; virtual;
function FindNumFormatInList(ANumFormatStr: String): Integer; function FindNumFormatInList(ANumFormatStr: String): Integer;
// function FixColor(AColor: TsColor): TsColor; virtual;
procedure FixFormat(ACell: PCell); virtual;
procedure GetSheetDimensions(AWorksheet: TsBasicWorksheet; procedure GetSheetDimensions(AWorksheet: TsBasicWorksheet;
out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual; out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual;
procedure ListAllNumFormats; virtual; procedure ListAllNumFormats; virtual;
@ -624,21 +622,6 @@ begin
Result := -1; Result := -1;
end; end;
{@@ ----------------------------------------------------------------------------
If formatting features of a cell are not supported by the destination file
format of the writer, here is the place to apply replacements.
Must be overridden by descendants, nothin happens here. See BIFF2.
@param ACell Pointer to the cell being investigated. Note that this cell
does not belong to the workbook, but is a cell of the
FFormattingStyles array.
-------------------------------------------------------------------------------}
procedure TsCustomSpreadWriter.FixFormat(ACell: PCell);
begin
Unused(ACell);
// to be overridden
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Determines the size of the worksheet to be written. VirtualMode is respected. Determines the size of the worksheet to be written. VirtualMode is respected.
Is called when the writer needs the size for output. Column and row count Is called when the writer needs the size for output. Column and row count

View File

@ -418,6 +418,11 @@ type
function GetLocalLinks(AWorksheet: TsBasicWorksheet): TsBiffExternSheetList; function GetLocalLinks(AWorksheet: TsBasicWorksheet): TsBiffExternSheetList;
end; end;
TsExcelNumFormatParser = class(TsNumFormatParser)
protected
function BuildFormatString: String; override;
end;
{ TsSpreadBIFFReader } { TsSpreadBIFFReader }
TsSpreadBIFFReader = class(TsCustomSpreadReader) TsSpreadBIFFReader = class(TsCustomSpreadReader)
@ -1062,6 +1067,20 @@ begin
end; end;
{ FPS can use an "ampm" modifier in the time format string, Excel cannot.
The function replaces is by "AM/PM". }
function TsExcelNumFormatParser.BuildFormatString: String;
var
p: Integer;
begin
Result := inherited;
if IsTimeFormat or IsDateTimeFormat then begin
p := pos('ampm', Lowercase(Result));
if p > 0 then Result := Copy(Result, 1, p-1) + 'AM/PM';
end;
end;
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
{ TsBIFFDefinedName } { TsBIFFDefinedName }
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
@ -4357,7 +4376,7 @@ begin
for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do
begin begin
fmtStr := NumFormatList[i]; fmtStr := NumFormatList[i];
parser := TsNumFormatParser.Create(fmtStr, Workbook.FormatSettings); parser := TsExcelNumFormatParser.Create(fmtStr, Workbook.FormatSettings);
try try
fmtStr := parser.FormatString; fmtStr := parser.FormatString;
WriteFORMAT(AStream, fmtStr, i); WriteFORMAT(AStream, fmtStr, i);

View File

@ -1823,6 +1823,7 @@ begin
if (uffNumberFormat in fmt^.UsedFormattingFields) then if (uffNumberFormat in fmt^.UsedFormattingFields) then
begin begin
nfp := book.GetNumberFormat(fmt^.NumberFormatIndex); nfp := book.GetNumberFormat(fmt^.NumberFormatIndex);
nfp.AllowLocalizedAMPM := false; // Replace "AMPM" by "AM/PM"
AppendToStream(AStream, Format(INDENT3 + AppendToStream(AStream, Format(INDENT3 +
'<NumberFormat ss:Format="%s"/>' + LF, [UTF8TextToXMLText(nfp.NumFormatStr)])); '<NumberFormat ss:Format="%s"/>' + LF, [UTF8TextToXMLText(nfp.NumFormatStr)]));
end; end;

View File

@ -3363,7 +3363,7 @@ begin
for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do
begin begin
numFmtStr := NumFormatList[i]; numFmtStr := NumFormatList[i];
parser := TsNumFormatParser.Create(numFmtStr, Workbook.FormatSettings); parser := TsExcelNumFormatParser.Create(numFmtStr, Workbook.FormatSettings);
try try
numFmtStr := UTF8TextToXMLText(parser.FormatString); numFmtStr := UTF8TextToXMLText(parser.FormatString);
xmlStr := xmlStr + Format('<numFmt numFmtId="%d" formatCode="%s" />', xmlStr := xmlStr + Format('<numFmt numFmtId="%d" formatCode="%s" />',