diff --git a/components/fpspreadsheet/source/common/fpsnumformat.pas b/components/fpspreadsheet/source/common/fpsnumformat.pas index 8cf6c5f16..3f70b0872 100644 --- a/components/fpspreadsheet/source/common/fpsnumformat.pas +++ b/components/fpspreadsheet/source/common/fpsnumformat.pas @@ -161,12 +161,14 @@ type by the number format parser from a format string. } TsNumFormatParams = class(TObject) private + FAllowLocalizedAMPM: Boolean; protected function GetNumFormat: TsNumberFormat; virtual; function GetNumFormatStr: String; virtual; public {@@ Array of the format sections } Sections: TsNumFormatSections; + constructor Create; procedure DeleteElement(ASectionIndex, AElementIndex: Integer); procedure InsertElement(ASectionIndex, AElementIndex: Integer; AToken: TsNumFormatToken); @@ -175,6 +177,7 @@ type procedure SetDecimals(AValue: Byte); procedure SetNegativeRed(AEnable: Boolean); procedure SetThousandSep(AEnable: Boolean); + property AllowLocalizedAMPM: boolean read FAllowLocalizedAMPM write FAllowLocalizedAMPM; property NumFormat: TsNumberFormat read GetNumFormat; property NumFormatStr: String read GetNumFormatStr; end; @@ -315,7 +318,8 @@ function BuildNumberFormatString(ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; ADecimals: Integer = -1; 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 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 - replacing "AM" or "PM" in the final formatted number are taken from the - TimeAMString or TimePMString of the specified FormatSettings. + Adds an AM/PM format code to a pre-built time formatting string. @param ATimeFormatString String of time formatting codes (such as 'hh:nn') @param AFormatSettings FormatSettings for locale-dependent information @@ -1130,13 +1132,8 @@ end; -------------------------------------------------------------------------------} function AddAMPM(const ATimeFormatString: String; const AFormatSettings: TFormatSettings): String; -var - am, pm: String; - fs: TFormatSettings absolute AFormatSettings; begin - am := IfThen(fs.TimeAMString <> '', fs.TimeAMString, 'AM'); - pm := IfThen(fs.TimePMString <> '', fs.TimePMString, 'PM'); - Result := Format('%s %s/%s', [StripAMPM(ATimeFormatString), am, pm]); + Result := Format('%s AM/PM', [StripAMPM(ATimeFormatString)]); end; {@@ ---------------------------------------------------------------------------- @@ -1470,9 +1467,15 @@ end; @param ASection Parsed section of number format elements as created by the number format parser - @return Excel-compatible format string + @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 -------------------------------------------------------------------------------} -function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; +function BuildFormatStringFromSection(const ASection: TsNumFormatSection; + AllowLocalizedAMPM: Boolean = true): String; var element: TsNumFormatElement; i, n: Integer; @@ -1542,7 +1545,12 @@ begin else Result := Result + DupeString('s', element.IntValue); nftMilliseconds: 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; nftCurrSymbol: if element.TextValue <> '' then @@ -2189,6 +2197,12 @@ end; { TsNumFormatParams } {==============================================================================} +constructor TsNumFormatParams.Create; +begin + inherited; + FAllowLocalizedAMPM := true; +end; + {@@ ---------------------------------------------------------------------------- Deletes a parsed number format element from the specified format section. @@ -2249,7 +2263,7 @@ begin if Length(Sections) > 0 then begin Result := BuildFormatStringFromSection(Sections[0]); for i := 1 to High(Sections) do - Result := Result + ';' + BuildFormatStringFromSection(Sections[i]); + Result := Result + ';' + BuildFormatStringFromSection(Sections[i], FAllowLocalizedAMPM); end else Result := ''; end; diff --git a/components/fpspreadsheet/source/common/fpsreaderwriter.pas b/components/fpspreadsheet/source/common/fpsreaderwriter.pas index 6bc65c480..c54af67f7 100644 --- a/components/fpspreadsheet/source/common/fpsreaderwriter.pas +++ b/components/fpspreadsheet/source/common/fpsreaderwriter.pas @@ -152,8 +152,6 @@ type procedure AddBuiltinNumFormats; virtual; function FindNumFormatInList(ANumFormatStr: String): Integer; -// function FixColor(AColor: TsColor): TsColor; virtual; - procedure FixFormat(ACell: PCell); virtual; procedure GetSheetDimensions(AWorksheet: TsBasicWorksheet; out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual; procedure ListAllNumFormats; virtual; @@ -624,21 +622,6 @@ begin Result := -1; 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. Is called when the writer needs the size for output. Column and row count diff --git a/components/fpspreadsheet/source/common/xlscommon.pas b/components/fpspreadsheet/source/common/xlscommon.pas index 43d11db32..484f67f50 100644 --- a/components/fpspreadsheet/source/common/xlscommon.pas +++ b/components/fpspreadsheet/source/common/xlscommon.pas @@ -418,6 +418,11 @@ type function GetLocalLinks(AWorksheet: TsBasicWorksheet): TsBiffExternSheetList; end; + TsExcelNumFormatParser = class(TsNumFormatParser) + protected + function BuildFormatString: String; override; + end; + { TsSpreadBIFFReader } TsSpreadBIFFReader = class(TsCustomSpreadReader) @@ -1062,6 +1067,20 @@ begin 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 } {------------------------------------------------------------------------------} @@ -4357,7 +4376,7 @@ begin for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do begin fmtStr := NumFormatList[i]; - parser := TsNumFormatParser.Create(fmtStr, Workbook.FormatSettings); + parser := TsExcelNumFormatParser.Create(fmtStr, Workbook.FormatSettings); try fmtStr := parser.FormatString; WriteFORMAT(AStream, fmtStr, i); diff --git a/components/fpspreadsheet/source/common/xlsxml.pas b/components/fpspreadsheet/source/common/xlsxml.pas index 35b54aa3d..e7a0facfb 100644 --- a/components/fpspreadsheet/source/common/xlsxml.pas +++ b/components/fpspreadsheet/source/common/xlsxml.pas @@ -1823,6 +1823,7 @@ begin if (uffNumberFormat in fmt^.UsedFormattingFields) then begin nfp := book.GetNumberFormat(fmt^.NumberFormatIndex); + nfp.AllowLocalizedAMPM := false; // Replace "AMPM" by "AM/PM" AppendToStream(AStream, Format(INDENT3 + '' + LF, [UTF8TextToXMLText(nfp.NumFormatStr)])); end; diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas index 60e537170..35c6be988 100644 --- a/components/fpspreadsheet/source/common/xlsxooxml.pas +++ b/components/fpspreadsheet/source/common/xlsxooxml.pas @@ -3363,7 +3363,7 @@ begin for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do begin numFmtStr := NumFormatList[i]; - parser := TsNumFormatParser.Create(numFmtStr, Workbook.FormatSettings); + parser := TsExcelNumFormatParser.Create(numFmtStr, Workbook.FormatSettings); try numFmtStr := UTF8TextToXMLText(parser.FormatString); xmlStr := xmlStr + Format('',