diff --git a/components/fpspreadsheet/examples/fpsctrls/main.pas b/components/fpspreadsheet/examples/fpsctrls/main.pas index 3c0c5ffb0..b73fa2c68 100644 --- a/components/fpspreadsheet/examples/fpsctrls/main.pas +++ b/components/fpspreadsheet/examples/fpsctrls/main.pas @@ -321,7 +321,7 @@ begin 7: WorkbookSource.FileFormat := sfOpenDocument; // Open/LibreOffice 8: WorkbookSource.FileFormat := sfCSV; // Text files end; - WorkbookSource.FileName := AcFileOpen.Dialog.FileName; // this loads the file + WorkbookSource.FileName := UTF8ToAnsi(AcFileOpen.Dialog.FileName); // this loads the file UpdateCaption; end; @@ -341,7 +341,7 @@ begin 6: fmt := sfCSV; 7: fmt := sfWikiTable_WikiMedia; end; - WorkbookSource.SaveToSpreadsheetFile(AcFileSaveAs.Dialog.FileName, fmt); + WorkbookSource.SaveToSpreadsheetFile(UTF8ToAnsi(AcFileSaveAs.Dialog.FileName), fmt); UpdateCaption; finally Screen.Cursor := crDefault; @@ -401,7 +401,7 @@ begin Caption := 'demo_ctrls' else Caption := Format('demo_ctrls - "%s" [%s]', [ - WorkbookSource.Filename, + AnsiToUTF8(WorkbookSource.Filename), GetFileFormatName(WorkbookSource.Workbook.FileFormat) ]); end; diff --git a/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas b/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas index f442919d6..55ddf8750 100644 --- a/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas +++ b/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas @@ -120,6 +120,8 @@ implementation { TForm1 } procedure TForm1.BtnLoadClick(Sender: TObject); +var + fn: ansistring; begin if OpenDialog.Execute then begin WorkbookSource.AutodetectFormat := false; @@ -136,16 +138,17 @@ begin end; // There are 3 possibilities to open a file: + fn := UTF8ToAnsi(OpenDialog.Filename); case CbLoader.ItemIndex of 0: if WorkbookSource.AutodetectFormat then - WorkbookSource.Workbook.ReadFromFile(OpenDialog.FileName) + WorkbookSource.Workbook.ReadFromFile(fn) else - WorkbookSource.Workbook.ReadFromFile(OpenDialog.Filename, WorkbookSource.FileFormat); - 1: WorkbookSource.FileName := OpenDialog.FileName; // this loads the file + WorkbookSource.Workbook.ReadFromFile(fn, WorkbookSource.FileFormat); + 1: WorkbookSource.FileName := fn; // this loads the file 2: if WorkbookSource.AutodetectFormat then - WorksheetGrid.LoadFromSpreadsheetFile(OpenDialog.FileName) + WorksheetGrid.LoadFromSpreadsheetFile(fn) else - WorksheetGrid.LoadFromSpreadsheetFile(OpenDialog.FileName, WorkbookSource.FileFormat); + WorksheetGrid.LoadFromSpreadsheetFile(fn, WorkbookSource.FileFormat); end; end; end; diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.pas b/components/fpspreadsheet/examples/fpsgrid/mainform.pas index 12f165e9c..8794027e7 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.pas +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.pas @@ -115,21 +115,22 @@ end; // Saves sheet in grid to file, overwriting existing file procedure TForm1.BtnSaveClick(Sender: TObject); var - err: String; + err, fn: String; begin if WorksheetGrid.Workbook = nil then exit; if WorksheetGrid.Workbook.Filename <>'' then begin - SaveDialog.InitialDir := ExtractFileDir(WorksheetGrid.Workbook.FileName); - SaveDialog.FileName := ChangeFileExt(ExtractFileName(WorksheetGrid.Workbook.FileName), ''); + fn := AnsiToUTF8(WorksheetGrid.Workbook.Filename); + SaveDialog.InitialDir := ExtractFileDir(fn); + SaveDialog.FileName := ChangeFileExt(ExtractFileName(fn), ''); end; if SaveDialog.Execute then begin Screen.Cursor := crHourglass; try - WorksheetGrid.SaveToSpreadsheetFile(SaveDialog.FileName); + WorksheetGrid.SaveToSpreadsheetFile(UTF8ToAnsi(SaveDialog.FileName)); finally Screen.Cursor := crDefault; // Show a message in case of error(s) diff --git a/components/fpspreadsheet/examples/fpsgrid_no_install/fpsgrid.lpr b/components/fpspreadsheet/examples/fpsgrid_no_install/fpsgrid.lpr index ab57e44ac..1752eb846 100644 --- a/components/fpspreadsheet/examples/fpsgrid_no_install/fpsgrid.lpr +++ b/components/fpspreadsheet/examples/fpsgrid_no_install/fpsgrid.lpr @@ -7,7 +7,7 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset - Forms, mainfrm, fpsHelpers + Forms, mainfrm, fpsCell { you can add units after this }; {$R *.res} diff --git a/components/fpspreadsheet/examples/fpsgrid_no_install/mainfrm.pas b/components/fpspreadsheet/examples/fpsgrid_no_install/mainfrm.pas index f5596e871..d91f9673a 100644 --- a/components/fpspreadsheet/examples/fpsgrid_no_install/mainfrm.pas +++ b/components/fpspreadsheet/examples/fpsgrid_no_install/mainfrm.pas @@ -92,21 +92,23 @@ end; procedure TForm1.BtnSaveClick(Sender: TObject); var err: String; + fn: String; begin if Grid.Workbook = nil then exit; if Grid.Workbook.Filename <>'' then begin - SaveDialog.InitialDir := ExtractFileDir(Grid.Workbook.FileName); - SaveDialog.FileName := ChangeFileExt(ExtractFileName(Grid.Workbook.FileName), ''); + fn := AnsiToUtf8(Grid.Workbook.Filename); + SaveDialog.InitialDir := ExtractFileDir(fn); + SaveDialog.FileName := ChangeFileExt(ExtractFileName(fn), ''); end; if SaveDialog.Execute then begin Screen.Cursor := crHourglass; try - Grid.SaveToSpreadsheetFile(SaveDialog.FileName); + Grid.SaveToSpreadsheetFile(UTF8ToAnsi(SaveDialog.FileName)); finally Screen.Cursor := crDefault; // Show a message in case of error(s) diff --git a/components/fpspreadsheet/examples/spready/mainform.lfm b/components/fpspreadsheet/examples/spready/mainform.lfm index 00f3bb5a2..f8f1f1694 100644 --- a/components/fpspreadsheet/examples/spready/mainform.lfm +++ b/components/fpspreadsheet/examples/spready/mainform.lfm @@ -498,7 +498,7 @@ object MainFrm: TMainFrm BorderStyle = bsNone ColCount = 27 MouseWheelOption = mwGrid - Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook, goFixedColSizing] + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook, goFixedColSizing, goCellHints] RowCount = 101 TabOrder = 1 TitleStyle = tsNative diff --git a/components/fpspreadsheet/examples/spready/mainform.pas b/components/fpspreadsheet/examples/spready/mainform.pas index 70926426e..96836145e 100644 --- a/components/fpspreadsheet/examples/spready/mainform.pas +++ b/components/fpspreadsheet/examples/spready/mainform.pas @@ -324,6 +324,7 @@ type procedure WorksheetGridHeaderClick(Sender: TObject; IsColumn: Boolean; Index: Integer); procedure WorksheetGridSelection(Sender: TObject; aCol, aRow: Integer); + private FCopiedFormat: TCell; procedure LoadFile(const AFileName: String); @@ -759,7 +760,7 @@ begin 7: fmt := sfWikiTable_wikimedia; end; try - WorksheetGrid.SaveToSpreadsheetFile(SaveDialog.FileName, fmt); + WorksheetGrid.SaveToSpreadsheetFile(Utf8ToAnsi(SaveDialog.FileName), fmt); finally Screen.Cursor := crDefault; err := WorksheetGrid.Workbook.ErrorMsg; @@ -1006,7 +1007,7 @@ begin end; procedure TMainFrm.LoadFile(const AFileName: String); -// Loads first worksheet from file into grid +// Loads first worksheet from file into grid. File name is UTF8. var err: String; begin @@ -1014,7 +1015,7 @@ begin Screen.Cursor := crHourglass; try try - WorksheetGrid.LoadFromSpreadsheetFile(UTF8ToSys(AFileName)); + WorksheetGrid.LoadFromSpreadsheetFile(utf8ToAnsi(AFileName)); except on E: Exception do begin // In an error occurs show at least an empty valid worksheet diff --git a/components/fpspreadsheet/fpsactions.pas b/components/fpspreadsheet/fpsactions.pas index 60774c062..bfc546553 100644 --- a/components/fpspreadsheet/fpsactions.pas +++ b/components/fpspreadsheet/fpsactions.pas @@ -1248,9 +1248,9 @@ procedure TsCellCommentAction.ExecuteTarget(Target: TObject); var txt: String; cellStr: String; - x, y: Integer; - R: TRect; begin + Unused(Target); + if Worksheet = nil then exit; @@ -1275,6 +1275,8 @@ end; procedure TsCellCommentAction.UpdateTarget(Target: TObject); begin + Unused(Target); + case FMode of ccmNew : Enabled := (Worksheet <> nil) and (Length(GetSelection) > 0); ccmEdit, diff --git a/components/fpspreadsheet/fpscsv.pas b/components/fpspreadsheet/fpscsv.pas index d82487343..83d887866 100644 --- a/components/fpspreadsheet/fpscsv.pas +++ b/components/fpspreadsheet/fpscsv.pas @@ -65,7 +65,7 @@ type LineEnding: TsCSVLineEnding; // W: Specification for line ending to be written Delimiter: Char; // RW: Column delimiter QuoteChar: Char; // RW: Character for quoting texts - Encoding: String; // RW: Encoding of file + Encoding: String; // RW: Encoding of file (code page, such as "utf8", "cp1252" etc) DetectContentType: Boolean; // R: try to convert strings to content types NumberFormat: String; // W: if empty write numbers like in sheet, otherwise use this format AutoDetectNumberFormat: Boolean; // R: automatically detects decimal/thousand separator used in numbers @@ -202,8 +202,6 @@ end; constructor TsCSVReader.Create(AWorkbook: TsWorkbook); begin inherited Create(AWorkbook); - FFormatSettings := CSVParams.FormatSettings; - ReplaceFormatSettings(FFormatSettings, AWorkbook.FormatSettings); FWorksheetName := 'Sheet1'; // will be replaced by filename end; @@ -455,6 +453,9 @@ begin FWorkbook := AData; FWorksheet := AData.AddWorksheet(FWorksheetName, true); + FFormatSettings := CSVParams.FormatSettings; + ReplaceFormatSettings(FFormatSettings, FWorkbook.FormatSettings); + // Create csv parser, read file and store in worksheet Parser := TCSVParser.Create; try diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 43ec7d180..00d02db30 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -59,9 +59,9 @@ type MergeBase: PCell; // Upper left cell of a merged range Comment: String; // Comment attached to the cell { Cell content } - UTF8StringValue: String; // strings cannot be part of a variant record + UTF8StringValue: String; // Strings cannot be part of a variant record FormulaValue: String; - case ContentType: TCellContentType of // must be at the end of the declaration + case ContentType: TCellContentType of // variant part must be at the end cctEmpty : (); // has no data at all cctFormula : (); // FormulaValue is outside the variant record cctNumber : (Numbervalue: Double); @@ -187,9 +187,9 @@ type procedure UpdateCaches; { Reading of values } - function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload; - function ReadAsUTF8Text(ACell: PCell): ansistring; overload; - function ReadAsUTF8Text(ACell: PCell; AFormatSettings: TFormatSettings): ansistring; overload; + function ReadAsUTF8Text(ARow, ACol: Cardinal): string; overload; //ansistring; overload; + function ReadAsUTF8Text(ACell: PCell): string; overload; //ansistring; overload; + function ReadAsUTF8Text(ACell: PCell; AFormatSettings: TFormatSettings): string; overload; //ansistring; overload; function ReadAsNumber(ARow, ACol: Cardinal): Double; overload; function ReadAsNumber(ACell: PCell): Double; overload; function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload; @@ -555,7 +555,8 @@ type private { Internal data } FWorksheets: TFPList; - FEncoding: TsEncoding; + FCodePage: String; +// FEncoding: TsEncoding; FFormat: TsSpreadsheetFormat; FBuiltinFontCount: Integer; FPalette: array of TsColorValue; @@ -694,7 +695,8 @@ type property ActiveWorksheet: TsWorksheet read FActiveWorksheet; {@@ This property is only used for formats which don't support unicode and support a single encoding for the whole document, like Excel 2 to 5 } - property Encoding: TsEncoding read FEncoding write FEncoding; + property CodePage: String read FCodePage write FCodepage; +// property Encoding: TsEncoding read FEncoding write FEncoding; {@@ Retrieves error messages collected during reading/writing } property ErrorMsg: String read GetErrorMsg; {@@ Filename of the saved workbook } @@ -2446,13 +2448,13 @@ end; Reads the contents of a cell and returns an user readable text representing the contents of the cell. - The resulting ansistring is UTF-8 encoded. + The resulting string is UTF-8 encoded. @param ARow The row of the cell @param ACol The column of the cell @return The text representation of the cell -------------------------------------------------------------------------------} -function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; +function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): string; //ansistring; begin Result := ReadAsUTF8Text(GetCell(ARow, ACol)); end; @@ -2461,21 +2463,21 @@ end; Reads the contents of a cell and returns an user readable text representing the contents of the cell. - The resulting ansistring is UTF-8 encoded. + The resulting string is UTF-8 encoded. @param ACell Pointer to the cell @return The text representation of the cell -------------------------------------------------------------------------------} -function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; +function TsWorksheet.ReadAsUTF8Text(ACell: PCell): string; //ansistring; begin Result := ReadAsUTF8Text(ACell, FWorkbook.FormatSettings); end; function TsWorksheet.ReadAsUTF8Text(ACell: PCell; - AFormatSettings: TFormatSettings): ansistring; + AFormatSettings: TFormatSettings): string; //ansistring; function FloatToStrNoNaN(const AValue: Double; - ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring; + ANumberFormat: TsNumberFormat; ANumberFormatStr: string): string; //ansistring; begin if IsNan(AValue) then Result := '' @@ -2493,7 +2495,7 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell; end; function DateTimeToStrNoNaN(const Value: Double; - ANumberFormat: TsNumberFormat; ANumberFormatStr: String): ansistring; + ANumberFormat: TsNumberFormat; ANumberFormatStr: String): string; //ansistring; var fmtp, fmtn, fmt0: String; begin @@ -6424,11 +6426,14 @@ end; constructor TsWorkbook.Create; var fmt: TsCellFormat; + cp: String; begin inherited Create; FWorksheets := TFPList.Create; FLog := TStringList.Create; FFormat := sfExcel8; + FCodePage := GetDefaultTextEncoding; +// FEncoding := seLatin1; FormatSettings := UTF8FormatSettings; FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat); @@ -8736,6 +8741,7 @@ end; -------------------------------------------------------------------------------} procedure TsCustomSpreadWriter.WriteComment(AStream: TStream; ACell: PCell); begin + Unused(AStream, ACell); end; {@@ ---------------------------------------------------------------------------- diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index 96ff39c49..492ca29e3 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -1845,7 +1845,6 @@ var lCell: PCell; justif: Byte; fmt: PsCellFormat; - savedBrushColor: TColor; begin if (Worksheet = nil) then exit; diff --git a/components/fpspreadsheet/fpsrpn.pas b/components/fpspreadsheet/fpsrpn.pas index 5ea43f2fb..a184b13ef 100644 --- a/components/fpspreadsheet/fpsrpn.pas +++ b/components/fpspreadsheet/fpsrpn.pas @@ -10,7 +10,7 @@ unit fpsRPN; {$ifdef fpc} - {$mode delphi}{$H+} + {$mode objfpc}{$H+} {$endif} interface @@ -436,6 +436,7 @@ begin while item <> nil do begin nextitem := item^.Next; Result[n] := item^.FE; + Result[n].StringValue := item^.FE.StringValue; if AReverse then dec(n) else inc(n); DisposeRPNItem(item); item := nextitem; diff --git a/components/fpspreadsheet/fpsstrings.pas b/components/fpspreadsheet/fpsstrings.pas index 5082c83e9..60d6e858a 100644 --- a/components/fpspreadsheet/fpsstrings.pas +++ b/components/fpspreadsheet/fpsstrings.pas @@ -52,6 +52,7 @@ resourcestring 'text found in cell %s.'; rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).'; rsAmbiguousDecThouSeparator = 'Assuming usage of decimal separator in "%s".'; + rsCodePageNotSupported = 'Code page "%s" is not supported. Using "cp1252" (Latin 1) instead.'; rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings? diff --git a/components/fpspreadsheet/fpstypes.pas b/components/fpspreadsheet/fpstypes.pas index 631877524..d17b16dd2 100644 --- a/components/fpspreadsheet/fpstypes.pas +++ b/components/fpspreadsheet/fpstypes.pas @@ -48,7 +48,7 @@ const type - + (* {@@ Possible encodings for a non-unicode encoded text } TsEncoding = ( seLatin1, @@ -57,8 +57,9 @@ type seGreek, seTurkish, seHebrew, - seArabic - ); + seArabic, + seUTF16 + ); *) {@@ Tokens to identify the elements in an expanded formula. @@ -110,7 +111,7 @@ type ElementKind: TFEKind; Row, Row2: Cardinal; // zero-based Col, Col2: Cardinal; // zero-based - Param1, Param2: Word; // Extra parameters +// Param1, Param2: Word; // Extra parameters DoubleValue: double; IntValue: Word; StringValue: String; @@ -228,7 +229,7 @@ type Due to limitations of the text mode the characters are not rotated here. There is, however, also a "stacked" variant which looks exactly like - the former case. + the 90-degrees-clockwise case. } TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation, rt90DegreeCounterClockwiseRotation, rtStacked); diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas index f92d30291..cd17afadd 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas +++ b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas @@ -228,7 +228,7 @@ begin SetLength(sa, ls); ANumbytes := ls*SizeOf(AnsiChar) + ALenBytes + 1; Move(FBuffer[ABufIndex + ALenBytes + 1], sa[1], ls*SizeOf(AnsiChar)); - AString := sa; + AString := AnsiToUTF8(sa); end else begin SetLength(sw, ls); ANumBytes := ls*SizeOf(WideChar) + ALenBytes + 1; diff --git a/components/fpspreadsheet/tests/datetests.pas b/components/fpspreadsheet/tests/datetests.pas index bc3a2ad06..39ac20804 100644 --- a/components/fpspreadsheet/tests/datetests.pas +++ b/components/fpspreadsheet/tests/datetests.pas @@ -381,7 +381,9 @@ var ActualDateTime: TDateTime; Row: Cardinal; TempFile: string; //write xls/xml to this file and read back from it + ErrorMargin: TDateTime; begin + ErrorMargin := 1.0/(24*60*60*1000*100); // 0.01 ms TempFile:=NewTempFile; {// Not needed: use workbook.writetofile with overwrite=true if fileexists(TempFile) then @@ -397,7 +399,8 @@ begin // Some checks inside worksheet itself if not(MyWorkSheet.ReadAsDateTime(Row,0,ActualDateTime)) then Fail('Failed writing date time for cell '+CellNotation(MyWorkSheet,Row)); - CheckEquals(SollDates[Row],ActualDateTime,'Test date/time value mismatch cell '+CellNotation(MyWorksheet,Row)); + CheckEquals(SollDates[Row], ActualDateTime, + 'Test date/time value mismatch cell '+CellNotation(MyWorksheet,Row)); end; MyWorkBook.WriteToFile(TempFile, AFormat, true); finally @@ -420,7 +423,8 @@ begin begin if not(MyWorkSheet.ReadAsDateTime(Row,0,ActualDateTime)) then Fail('Could not read date time for cell '+CellNotation(MyWorkSheet,Row)); - CheckEquals(SollDates[Row],ActualDateTime,'Test date/time value mismatch cell '+CellNotation(MyWorkSheet,Row)); + CheckEquals(SollDates[Row], ActualDateTime, ErrorMargin, + 'Test date/time value mismatch cell '+CellNotation(MyWorkSheet,Row)); end; finally MyWorkbook.Free; diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index 0aaa75d33..af201c8de 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -48,6 +48,7 @@ + @@ -60,7 +61,6 @@ - @@ -69,21 +69,19 @@ - - + - @@ -96,15 +94,16 @@ + + - @@ -121,7 +120,6 @@ - @@ -130,7 +128,6 @@ - diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index 8532e5727..1867ed1d2 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -52,7 +52,7 @@ {!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! DO NOT CHANGE THIS FORMULA - ITS RESULT (-9) IS HARD-CODED IN OTHER TESTS !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!} - + (* // Add cell values - relative addresses inc(Row); formula := 'B1+B2'; @@ -3001,7 +3001,7 @@ SetLength(sollValues, Row+1); sollValues[Row] := StringResult('Ha'); Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString); - + *) // LEFT (2 parameters, utf8) inc(Row); formula := 'LEFT("Ändern",3)'; diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index d6a51bfef..35e5c829d 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -57,7 +57,7 @@ type TsSpreadBIFF2Reader = class(TsSpreadBIFFReader) private - WorkBookEncoding: TsEncoding; +// WorkBookEncoding: TsEncoding; FFont: TsFont; protected procedure CreateNumFormatList; override; @@ -110,6 +110,8 @@ type ACell: PCell); override; procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell); override; +// procedure WriteCodePage(AStream: TStream; AEncoding: TsEncoding); override; + procedure WriteCodePage(AStream: TStream; ACodePage: String); override; procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell); override; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; @@ -512,7 +514,7 @@ begin FWorkbook.RemoveAllFonts; { Store some data about the workbook that other routines need } - WorkBookEncoding := AData.Encoding; + //WorkBookEncoding := AData.Encoding; BIFF2EOF := False; @@ -531,6 +533,7 @@ begin case RecordType of INT_EXCEL_ID_BLANK : ReadBlank(AStream); INT_EXCEL_ID_BOOLERROR : ReadBool(AStream); + INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream); INT_EXCEL_ID_NOTE : ReadComment(AStream); INT_EXCEL_ID_FONT : ReadFont(AStream); INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream); @@ -648,8 +651,8 @@ var L: Byte; ARow, ACol: Cardinal; XF: Word; - AValue: ansistring; - AStrValue: UTF8String; + ansiStr: ansistring; + valueStr: UTF8String; cell: PCell; begin { Read entire record, starting at Row, except for string data } @@ -661,10 +664,12 @@ begin { String with 8-bit size } L := rec.TextLen; - SetLength(AValue, L); - AStream.ReadBuffer(AValue[1], L); + SetLength(ansiStr, L); + AStream.ReadBuffer(ansiStr[1], L); { Save the data } + valueStr := ConvertEncoding(ansiStr, FCodePage, encodingUTF8); + { case WorkBookEncoding of seLatin2: AStrValue := CP1250ToUTF8(AValue); seCyrillic: AStrValue := CP1251ToUTF8(AValue); @@ -676,6 +681,7 @@ begin // Latin 1 is the default AStrValue := CP1252ToUTF8(AValue); end; + } { Create cell } if FIsVirtualMode then begin @@ -683,7 +689,7 @@ begin cell := @FVirtualCell; end else cell := FWorksheet.GetCell(ARow, ACol); - FWorksheet.WriteUTF8Text(cell, AStrValue); + FWorksheet.WriteUTF8Text(cell, valueStr); { Apply formatting to cell } ApplyCellFormatting(cell, XF); @@ -870,7 +876,8 @@ begin begin // The "IncompleteCell" has been identified in the sheet when reading // the FORMULA record which precedes the String record. - FIncompleteCell^.UTF8StringValue := AnsiToUTF8(s); +// FIncompleteCell^.UTF8StringValue := AnsiToUTF8(s); + FIncompleteCell^.UTF8StringValue := ConvertEncoding(s, FCodePage, encodingUTF8); FIncompleteCell^.ContentType := cctUTF8String; if FIsVirtualMode then Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell); @@ -1141,6 +1148,32 @@ begin AStream.WriteBuffer(rec, SizeOf(rec)); end; +{@@ ---------------------------------------------------------------------------- + Writes an Excel 2 CODEPAGE record +-------------------------------------------------------------------------------} +procedure TsSpreadBIFF2Writer.WriteCodePage(AStream: TStream; ACodePage: String); +// AEncoding: TsEncoding); +begin + if ACodePage = 'cp1251' then begin + AStream.WriteWord(WordToLE(INT_EXCEL_ID_CODEPAGE)); + AStream.WriteWord(WordToLE(2)); + AStream.WriteWord(WordToLE(WORD_CP_1258_Latin1_BIFF2_3)); + FCodePage := ACodePage; + end else + inherited; + (* + if AEncoding = seLatin1 then begin + cp := WORD_CP_1258_Latin1_BIFF2_3; + FCodePage := 'cp1252'; + + { BIFF Record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_CODEPAGE)); + AStream.WriteWord(WordToLE(2)); + AStream.WriteWord(WordToLE(cp)); + end else + inherited; *) +end; + {@@ ---------------------------------------------------------------------------- Writes an Excel 2 COLWIDTH record -------------------------------------------------------------------------------} @@ -1245,6 +1278,7 @@ begin WriteBOF(AStream); WriteFonts(AStream); + WriteCodePage(AStream, Workbook.CodePage); //Encoding); WriteFormatCount(AStream); WriteNumFormats(AStream); WriteXFRecords(AStream); @@ -1531,7 +1565,7 @@ var begin Unused(ANumFormatData); - s := NumFormatList.FormatStringForWriting(AListIndex); + s := ConvertEncoding(NumFormatList.FormatStringForWriting(AListIndex), encodingUTF8, FCodePage); len := Length(s); { BIFF record header } diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index a342bae23..da1a39c93 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -34,19 +34,15 @@ EOF The row and column numbering in BIFF files is zero-based. Excel file format specification obtained from: - -http://sc.openoffice.org/excelfileformat.pdf + http://sc.openoffice.org/excelfileformat.pdf Records Needed to Make a BIFF5 File Microsoft Excel Can Use obtained from: - -http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q147732&ID=KB;EN-US;Q147732&LN=EN-US&rnk=2&SD=msdn&FR=0&qry=BIFF&src=DHCS_MSPSS_msdn_SRCH&SPR=MSALL& + http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q147732&ID=KB;EN-US;Q147732&LN=EN-US&rnk=2&SD=msdn&FR=0&qry=BIFF&src=DHCS_MSPSS_msdn_SRCH&SPR=MSALL& Microsoft BIFF 5 writer example: + http://support.microsoft.com/kb/150447/en-us -http://support.microsoft.com/kb/150447/en-us - -Encoding information: ISO_8859_1 is used, to have support to -other characters, please use a format which support unicode +Encoding information: as specified fy the Encoding property of the workbook. AUTHORS: Felipe Monteiro de Carvalho } @@ -101,8 +97,6 @@ type { TsSpreadBIFF5Writer } TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter) - private - WorkBookEncoding: TsEncoding; protected { Record writing methods } procedure WriteBOF(AStream: TStream; ADataType: Word); @@ -353,6 +347,7 @@ begin case RecordType of INT_EXCEL_ID_BOF : ; INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream); + INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream); INT_EXCEL_ID_FONT : ReadFont(AStream); INT_EXCEL_ID_FORMAT : ReadFormat(AStream); INT_EXCEL_ID_XF : ReadXF(AStream); @@ -481,7 +476,8 @@ begin SetLength(s, Len); AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar)); - sheetName := AnsiToUTF8(s); +// sheetName := AnsiToUTF8(s); + sheetName := ConvertEncoding(s, FCodePage, EncodingUTF8); FWorksheetNames.Add(sheetName); end; @@ -550,7 +546,8 @@ begin SetLength(s, Len); AStream.ReadBuffer(s[1], len); if (FIncompleteCell <> nil) and (s <> '') then begin - FIncompleteCell^.UTF8StringValue := AnsiToUTF8(s); +// FIncompleteCell^.UTF8StringValue := AnsiToUTF8(s); + FIncompletecell^.UTF8StringValue := ConvertEncoding(s, FCodePage, encodingUTF8); FIncompleteCell^.ContentType := cctUTF8String; if FIsVirtualMode then Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell); @@ -720,7 +717,6 @@ end; procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook); var BIFF5EOF: Boolean; - p,s: Int64; begin { Initializations } @@ -743,8 +739,6 @@ begin ReadWorksheet(AStream, AData); // Check for the end of the file - p := AStream.Position; - s := AStream.Size; if AStream.Position >= AStream.Size then BIFF5EOF := True; // Final preparations @@ -818,7 +812,7 @@ begin Len := AStream.ReadByte(); SetLength(fontname, Len); AStream.ReadBuffer(fontname[1], Len); - font.FontName := fontname; + font.FontName := ConvertEncoding(fontname, FCodePage, encodingUTF8); { Add font to workbook's font list } FWorkbook.AddFont(font); @@ -846,7 +840,8 @@ begin AStream.ReadBuffer(fmtString[1], len); // Add to the list - NumFormatList.AnalyzeAndAdd(fmtIndex, AnsiToUTF8(fmtString)); +// NumFormatList.AnalyzeAndAdd(fmtIndex, AnsiToUTF8(fmtString)); + NumFormatList.AnalyzeAndAdd(fmtIndex, ConvertEncoding(fmtString, FCodePage, encodingUTF8)); end; procedure TsSpreadBIFF5Reader.ReadLabel(AStream: TStream); @@ -856,7 +851,8 @@ var ARow, ACol: Cardinal; XF: WORD; cell: PCell; - AValue: ansistring; + ansistr: ansistring; + valuestr: String; begin rec.Row := 0; // to silence the compiler... @@ -868,8 +864,8 @@ begin { Byte String with 16-bit size } L := WordLEToN(rec.TextLen); - SetLength(AValue, L); - AStream.ReadBuffer(AValue[1], L); + SetLength(ansistr, L); + AStream.ReadBuffer(ansistr[1], L); { Create cell } if FIsVirtualMode then begin @@ -879,7 +875,8 @@ begin cell := FWorksheet.GetCell(ARow, ACol); { Save the data } - FWorksheet.WriteUTF8Text(cell, ISO_8859_1ToUTF8(AValue)); + valueStr := ConvertEncoding(ansistr, FCodePage, encodingUTF8); + FWorksheet.WriteUTF8Text(cell, valueStr); //ISO_8859_1ToUTF8(ansistr)); { Add attributes } ApplyCellFormatting(cell, XF); @@ -939,14 +936,11 @@ var i, len: Integer; pane: Byte; begin - { Store some data about the workbook that other routines need } - WorkBookEncoding := Workbook.Encoding; - { Write workbook globals } WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); - WriteCodepage(AStream, WorkBookEncoding); + WriteCodepage(AStream, Workbook.CodePage); //WorkBook.Encoding); WriteWindow1(AStream); WriteFonts(AStream); WriteNumFormats(AStream); @@ -1042,11 +1036,14 @@ end; function TsSpreadBIFF5Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; var Len: Byte; - LatinSheetName: string; + xlsSheetName: ansistring; begin + xlsSheetName := ConvertEncoding(ASheetName, encodingUTF8, FCodePage); + Len := Length(xlsSheetName); + { LatinSheetName := UTF8ToISO_8859_1(ASheetName); Len := Length(LatinSheetName); - + } { BIFF Record header } AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOUNDSHEET)); AStream.WriteWord(WordToLE(6 + 1 + Len)); @@ -1064,7 +1061,8 @@ begin { Sheet name: Byte string, 8-bit length } AStream.WriteByte(Len); - AStream.WriteBuffer(LatinSheetName[1], Len); +// AStream.WriteBuffer(LatinSheetName[1], Len); + AStream.WriteBuffer(xlsSheetName[1], Len); end; {@@ ---------------------------------------------------------------------------- @@ -1204,15 +1202,18 @@ type end; var len: Integer; - s: ansistring; + fmtStr: String; + ansiFmtStr: ansiString; rec: TNumFormatRecord; buf: array of byte; begin if (ANumFormatData = nil) or (ANumFormatData.FormatString = '') then exit; - s := UTF8ToAnsi(NumFormatList.FormatStringForWriting(AListIndex)); - len := Length(s); +// s := UTF8ToAnsi(NumFormatList.FormatStringForWriting(AListIndex)); + fmtStr := NumFormatList.FormatStringForWriting(AListIndex); + ansiFmtStr := ConvertEncoding(fmtStr, encodingUTF8, FCodePage); + len := Length(ansiFmtStr); { BIFF record header } rec.RecordID := WordToLE(INT_EXCEL_ID_FORMAT); @@ -1227,7 +1228,7 @@ begin { Copy the format string characters into a buffer immediately after rec } SetLength(buf, SizeOf(rec) + SizeOf(ansiChar)*len); Move(rec, buf[0], SizeOf(rec)); - Move(s[1], buf[SizeOf(rec)], len*SizeOf(ansiChar)); + Move(ansiFmtStr[1], buf[SizeOf(rec)], len*SizeOf(ansiChar)); { Write out } AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*len); @@ -1284,6 +1285,8 @@ begin if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; + ansiValue := ConvertEncoding(AValue, encodingUTF8, FCodePage); + { case WorkBookEncoding of seLatin2: AnsiValue := UTF8ToCP1250(AValue); seCyrillic: AnsiValue := UTF8ToCP1251(AValue); @@ -1295,6 +1298,7 @@ begin // Latin 1 is the default AnsiValue := UTF8ToCP1252(AValue); end; + } if AnsiValue = '' then begin // Bad formatted UTF8String (maybe ANSI?) @@ -1354,7 +1358,8 @@ var s: ansistring; len: Integer; begin - s := UTF8ToAnsi(AString); +// s := UTF8ToAnsi(AString); + s := ConvertEncoding(AString, encodingUTF8, FCodePage); len := Length(s); { BIFF Record header } diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index be30152c2..d73fa91c0 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -78,7 +78,7 @@ type procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook); procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook); procedure ReadBoundsheet(AStream: TStream); - function ReadString(const AStream: TStream; const ALength: WORD): UTF8String; + function ReadString(const AStream: TStream; const ALength: WORD): String; protected procedure ReadFont(const AStream: TStream); procedure ReadFormat(AStream: TStream); override; @@ -219,7 +219,7 @@ var implementation uses - Math, fpsStrings, fpsStreams, fpsExprParser; + Math, lconvencoding, fpsStrings, fpsStreams, fpsExprParser; const { Excel record IDs } @@ -349,81 +349,80 @@ var lLen: SizeInt; RecordType: WORD; RecordSize: WORD; - C: char; + C: WideChar; begin StringFlags:=AStream.ReadByte; Dec(PendingRecordSize); if StringFlags and 4 = 4 then begin //Asian phonetics //Read Asian phonetics Length (not used) - AsianPhoneticBytes:=DWordLEtoN(AStream.ReadDWord); + AsianPhoneticBytes := DWordLEtoN(AStream.ReadDWord); end; if StringFlags and 8 = 8 then begin //Rich string - RunsCounter:=WordLEtoN(AStream.ReadWord); + RunsCounter := WordLEtoN(AStream.ReadWord); dec(PendingRecordSize,2); end; if StringFlags and 1 = 1 Then begin //String is WideStringLE if (ALength*SizeOf(WideChar)) > PendingRecordSize then begin - SetLength(Result,PendingRecordSize div 2); - AStream.ReadBuffer(Result[1],PendingRecordSize); - Dec(PendingRecordSize,PendingRecordSize); + SetLength(Result, PendingRecordSize div 2); + AStream.ReadBuffer(Result[1], PendingRecordSize); + Dec(PendingRecordSize, PendingRecordSize); end else begin SetLength(Result, ALength); - AStream.ReadBuffer(Result[1],ALength * SizeOf(WideChar)); - Dec(PendingRecordSize,ALength * SizeOf(WideChar)); + AStream.ReadBuffer(Result[1], ALength * SizeOf(WideChar)); + Dec(PendingRecordSize, ALength * SizeOf(WideChar)); end; Result := WideStringLEToN(Result); end else begin - //String is 1 byte per char, this is UTF-16 with the high byte ommited because it is zero - //so decompress and then convert - lLen:=ALength; + // String is 1 byte per char, this is UTF-16 with the high byte ommited + // because it is zero, so decompress and then convert + lLen := ALength; SetLength(DecomprStrValue, lLen); for i := 1 to lLen do begin - C:=WideChar(AStream.ReadByte()); + C := WideChar(AStream.ReadByte); // Read 1 byte, but put it into a 2-byte char DecomprStrValue[i] := C; Dec(PendingRecordSize); - if (PendingRecordSize<=0) and (iINT_EXCEL_ID_CONTINUE then begin + if RecordType <> INT_EXCEL_ID_CONTINUE then begin Raise Exception.Create('[TsSpreadBIFF8Reader.ReadWideString] Expected CONTINUE record not found.'); end else begin - PendingRecordSize:=RecordSize; - DecomprStrValue:=copy(DecomprStrValue,1,i)+ReadWideString(AStream,ALength-i); + PendingRecordSize := RecordSize; + DecomprStrValue := copy(DecomprStrValue,1,i) + ReadWideString(AStream, ALength-i); break; end; end; end; - Result := DecomprStrValue; end; if StringFlags and 8 = 8 then begin //Rich string (This only happened in BIFF8) for j := 1 to RunsCounter do begin - if (PendingRecordSize<=0) then begin + if (PendingRecordSize <= 0) then begin //A CONTINUE may happend here RecordType := WordLEToN(AStream.ReadWord); RecordSize := WordLEToN(AStream.ReadWord); - if RecordType<>INT_EXCEL_ID_CONTINUE then begin + if RecordType <> INT_EXCEL_ID_CONTINUE then begin Raise Exception.Create('[TsSpreadBIFF8Reader.ReadWideString] Expected CONTINUE record not found.'); end else begin - PendingRecordSize:=RecordSize; + PendingRecordSize := RecordSize; end; end; AStream.ReadWord; AStream.ReadWord; - dec(PendingRecordSize,2*2); + dec(PendingRecordSize, 2*2); end; end; if StringFlags and 4 = 4 then begin //Asian phonetics //Read Asian phonetics, discarded as not used. - SetLength(AnsiStrValue,AsianPhoneticBytes); - AStream.ReadBuffer(AnsiStrValue[1],AsianPhoneticBytes); + SetLength(AnsiStrValue, AsianPhoneticBytes); + AStream.ReadBuffer(AnsiStrValue[1], AsianPhoneticBytes); end; end; @@ -572,7 +571,7 @@ begin end; function TsSpreadBIFF8Reader.ReadString(const AStream: TStream; - const ALength: WORD): UTF8String; + const ALength: WORD): String; begin Result := UTF16ToUTF8(ReadWideString(AStream, ALength)); end; @@ -618,18 +617,16 @@ begin BIFF8EOF := False; { Read workbook globals } - ReadWorkbookGlobals(AStream, AData); // Check for the end of the file if AStream.Position >= AStream.Size then BIFF8EOF := True; { Now read all worksheets } - while (not BIFF8EOF) do begin //Safe to not read beyond assigned worksheet names. - if FCurrentWorksheet>FWorksheetNames.Count-1 then break; + if FCurrentWorksheet > FWorksheetNames.Count-1 then break; ReadWorksheet(AStream, AData); @@ -647,9 +644,7 @@ begin FWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8)); { Finalizations } - FWorksheetNames.Free; - end; procedure TsSpreadBIFF8Reader.ReadLabel(AStream: TStream); @@ -956,20 +951,22 @@ end; { Helper function for reading a string with 8-bit length. } function TsSpreadBIFF8Reader.ReadString_8bitLen(AStream: TStream): String; +const + HAS_8BITLEN = true; var - s: widestring; + wideStr: widestring; begin - s := ReadWideString(AStream, true); - Result := UTF8Encode(s); + wideStr := ReadWideString(AStream, HAS_8BITLEN); + Result := UTF8Encode(wideStr); end; procedure TsSpreadBIFF8Reader.ReadStringRecord(AStream: TStream); var - s: String; + wideStr: WideString; begin - s := ReadWideString(AStream, false); - if (FIncompleteCell <> nil) and (s <> '') then begin - FIncompleteCell^.UTF8StringValue := UTF8Encode(s); + wideStr := ReadWideString(AStream, false); + if (FIncompleteCell <> nil) and (wideStr <> '') then begin + FIncompleteCell^.UTF8StringValue := UTF8Encode(wideStr); FIncompleteCell^.ContentType := cctUTF8String; if FIsVirtualMode then Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell); @@ -984,14 +981,12 @@ procedure TsSpreadBIFF8Reader.ReadXF(const AStream: TStream); begin case dw of $01..$07: result := TsLineStyle(dw-1); -// $07: Result := lsDotted; else Result := lsDashed; end; end; var rec: TBIFF8_XFRecord; fmt: TsCellFormat; -// xf: TXFRecord; b: Byte; dw: DWord; fill: Integer; @@ -1271,9 +1266,8 @@ var pane: Byte; begin { Write workbook globals } - WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); - + WriteCodePage(AStream, 'ucs2le'); //seUTF16); WriteWindow1(AStream); WriteFonts(AStream); WriteNumFormats(AStream); @@ -1293,7 +1287,6 @@ begin WriteEOF(AStream); { Write each worksheet } - for i := 0 to Workbook.GetWorksheetCount - 1 do begin FWorksheet := Workbook.GetWorksheetByIndex(i); @@ -1483,7 +1476,7 @@ begin if AFont.Size <= 0.0 then raise Exception.Create('Font size not specified.'); - WideFontName := AFont.FontName; + WideFontName := UTF8Decode(AFont.FontName); Len := Length(WideFontName); { BIFF Record header } diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index d6849050c..218bf630b 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -4,20 +4,13 @@ unit xlscommon; OpenOffice Microsoft Excel File Format document } {$ifdef fpc} - {$mode delphi}{$H+} + {$mode objfpc}{$H+} {$endif} interface uses Classes, SysUtils, DateUtils, - (* - {$ifdef USE_NEW_OLE} - fpolebasic, - {$else} - fpolestorage, - {$endif} - *) fpstypes, fpspreadsheet, fpsutils, lconvencoding; const @@ -73,6 +66,11 @@ const { CODEPAGE record constants } WORD_ASCII = 367; + WORD_CP_437_DOS_US = 437; + WORD_CP_850_DOS_Latin1 = 850; + WORD_CP_852_DOS_Latin2 = 852; + WORD_CP_866_DOS_Cyrillic = 866; + WORD_CP_874_Thai = 874; WORD_UTF_16 = 1200; // BIFF 8 WORD_CP_1250_Latin2 = 1250; WORD_CP_1251_Cyrillic = 1251; @@ -304,6 +302,7 @@ type FDateMode: TDateMode; FLastRow: Cardinal; FLastCol: Cardinal; + FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding procedure CreateNumFormatList; override; function FindXFIndex(ACell: PCell): Integer; virtual; function FixColor(AColor: TsColor): TsColor; override; @@ -321,7 +320,7 @@ type procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell); override; // Writes out used codepage for character encoding - procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding); + procedure WriteCodePage(AStream: TStream; ACodePage: String); virtual; // Writes out column info(s) procedure WriteColInfo(AStream: TStream; ACol: PCol); procedure WriteColInfos(AStream: TStream; ASheet: TsWorksheet); @@ -403,7 +402,7 @@ implementation uses AVL_Tree, Math, Variants, - {%H-}fpspatches, xlsConst, fpsNumFormatParser, fpsrpn, fpsExprParser; + {%H-}fpspatches, fpsStrings, xlsConst, fpsNumFormatParser, fpsrpn, fpsExprParser; const { Helper table for rpn formulas: @@ -827,49 +826,41 @@ begin lCodePage := WordLEToN(AStream.ReadWord()); case lCodePage of - // 016FH = 367 = ASCII - // 01B5H = 437 = IBM PC CP-437 (US) - //02D0H = 720 = IBM PC CP-720 (OEM Arabic) - //02E1H = 737 = IBM PC CP-737 (Greek) - //0307H = 775 = IBM PC CP-775 (Baltic) - //0352H = 850 = IBM PC CP-850 (Latin I) - $0352: FCodepage := 'cp850'; - //0354H = 852 = IBM PC CP-852 (Latin II (Central European)) - $0354: FCodepage := 'cp852'; - //0357H = 855 = IBM PC CP-855 (Cyrillic) - $0357: FCodepage := 'cp855'; - //0359H = 857 = IBM PC CP-857 (Turkish) - $0359: FCodepage := 'cp857'; - //035AH = 858 = IBM PC CP-858 (Multilingual Latin I with Euro) - //035CH = 860 = IBM PC CP-860 (Portuguese) - //035DH = 861 = IBM PC CP-861 (Icelandic) - //035EH = 862 = IBM PC CP-862 (Hebrew) - //035FH = 863 = IBM PC CP-863 (Canadian (French)) - //0360H = 864 = IBM PC CP-864 (Arabic) - //0361H = 865 = IBM PC CP-865 (Nordic) - //0362H = 866 = IBM PC CP-866 (Cyrillic (Russian)) - //0365H = 869 = IBM PC CP-869 (Greek (Modern)) - //036AH = 874 = Windows CP-874 (Thai) - //03A4H = 932 = Windows CP-932 (Japanese Shift-JIS) - //03A8H = 936 = Windows CP-936 (Chinese Simplified GBK) - //03B5H = 949 = Windows CP-949 (Korean (Wansung)) - //03B6H = 950 = Windows CP-950 (Chinese Traditional BIG5) - //04B0H = 1200 = UTF-16 (BIFF8) - $04B0: FCodepage := 'utf-16'; - //04E2H = 1250 = Windows CP-1250 (Latin II) (Central European) - //04E3H = 1251 = Windows CP-1251 (Cyrillic) - //04E4H = 1252 = Windows CP-1252 (Latin I) (BIFF4-BIFF5) - //04E5H = 1253 = Windows CP-1253 (Greek) - //04E6H = 1254 = Windows CP-1254 (Turkish) - $04E6: FCodepage := 'cp1254'; - //04E7H = 1255 = Windows CP-1255 (Hebrew) - //04E8H = 1256 = Windows CP-1256 (Arabic) - //04E9H = 1257 = Windows CP-1257 (Baltic) - //04EAH = 1258 = Windows CP-1258 (Vietnamese) - //0551H = 1361 = Windows CP-1361 (Korean (Johab)) - //2710H = 10000 = Apple Roman - //8000H = 32768 = Apple Roman - //8001H = 32769 = Windows CP-1252 (Latin I) (BIFF2-BIFF3) + // 016FH = 367 = ASCII + WORD_CP_437_DOS_US: FCodePage := 'cp437'; // IBM PC CP-437 (US) + //02D0H = 720 = IBM PC CP-720 (OEM Arabic) + //02E1H = 737 = IBM PC CP-737 (Greek) + //0307H = 775 = IBM PC CP-775 (Baltic) + WORD_CP_850_DOS_Latin1: FCodepage := 'cp850'; // IBM PC CP-850 (Latin I) + WORD_CP_852_DOS_Latin2: FCodepage := 'cp852'; // IBM PC CP-852 (Latin II (Central European)) + //035AH = 858 = IBM PC CP-858 (Multilingual Latin I with Euro) + //035CH = 860 = IBM PC CP-860 (Portuguese) + //035DH = 861 = IBM PC CP-861 (Icelandic) + //035EH = 862 = IBM PC CP-862 (Hebrew) + //035FH = 863 = IBM PC CP-863 (Canadian (French)) + //0360H = 864 = IBM PC CP-864 (Arabic) + //0361H = 865 = IBM PC CP-865 (Nordic) + WORD_CP_866_DOS_Cyrillic: FCodePage := 'cp866'; // IBM PC CP-866 (Cyrillic Russian) + //0365H = 869 = IBM PC CP-869 (Greek (Modern)) + WORD_CP_874_Thai: FCodePage := 'cp874'; // 874 = Windows CP-874 (Thai) + //03A4H = 932 = Windows CP-932 (Japanese Shift-JIS) + //03A8H = 936 = Windows CP-936 (Chinese Simplified GBK) + //03B5H = 949 = Windows CP-949 (Korean (Wansung)) + //03B6H = 950 = Windows CP-950 (Chinese Traditional BIG5) + WORD_UTF_16 : FCodePage := 'ucs2le'; // UTF-16 (BIFF8) + WORD_CP_1250_Latin2: FCodepage := 'cp1250'; // Windows CP-1250 (Latin II) (Central European) + WORD_CP_1251_Cyrillic: FCodePage := 'cp1251'; // Windows CP-1251 (Cyrillic) + WORD_CP_1252_Latin1: FCodePage := 'cp1252'; // Windows CP-1252 (Latin I) (BIFF4-BIFF5) + WORD_CP_1253_Greek: FCodePage := 'cp1253'; // Windows CP-1253 (Greek) + WORD_CP_1254_Turkish: FCodepage := 'cp1254'; // Windows CP-1254 (Turkish) + WORD_CP_1255_Hebrew: FCodePage := 'cp1255'; // Windows CP-1255 (Hebrew) + WORD_CP_1256_Arabic: FCodePage := 'cp1256'; // Windows CP-1256 (Arabic) + WORD_CP_1257_Baltic: FCodePage := 'cp1257'; // Windows CP-1257 (Baltic) + WORD_CP_1258_Vietnamese: FCodePage := 'cp1258'; // Windows CP-1258 (Vietnamese) + //0551H = 1361 = Windows CP-1361 (Korean (Johab)) + //2710H = 10000 = Apple Roman + //8000H = 32768 = Apple Roman + WORD_CP_1258_Latin1_BIFF2_3: FCodePage := 'cp1252'; // Windows CP-1252 (Latin I) (BIFF2-BIFF3) end; end; @@ -1711,7 +1702,8 @@ begin len := AStream.ReadByte; SetLength(s, len); AStream.ReadBuffer(s[1], len); - Result := ansiToUTF8(s); + Result := ConvertEncoding(s, FCodePage, encodingUTF8); +// Result := ansiToUTF8(s); end; { Reads a STRING record. It immediately precedes a FORMULA record which has a @@ -1806,7 +1798,7 @@ end; function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer; begin FLastRow := 0; - IterateThroughCells(nil, AWorksheet.Cells, GetLastRowCallback); + IterateThroughCells(nil, AWorksheet.Cells, @GetLastRowCallback); Result := FLastRow; end; @@ -1819,7 +1811,7 @@ end; function TsSpreadBIFFWriter.GetLastColIndex(AWorksheet: TsWorksheet): Word; begin FLastCol := 0; - IterateThroughCells(nil, AWorksheet.Cells, GetLastColCallback); + IterateThroughCells(nil, AWorksheet.Cells, @GetLastColCallback); Result := FLastCol; end; @@ -1878,28 +1870,44 @@ begin AStream.WriteBuffer(rec, SizeOf(rec)); end; -procedure TsSpreadBIFFWriter.WriteCodepage(AStream: TStream; - AEncoding: TsEncoding); +{@@ ---------------------------------------------------------------------------- + Writes the code page identifier defined by the workbook to the stream. + BIFF2 has to be overridden because is uses cp1252, but has a different + number code. +-------------------------------------------------------------------------------} +procedure TsSpreadBIFFWriter.WriteCodepage(AStream: TStream; ACodePage: String); +// AEncoding: TsEncoding); var - lCodepage: Word; + cp: Word; begin { BIFF Record header } AStream.WriteWord(WordToLE(INT_EXCEL_ID_CODEPAGE)); AStream.WriteWord(WordToLE(2)); { Codepage } - case AEncoding of - seLatin2: lCodepage := WORD_CP_1250_Latin2; - seCyrillic: lCodepage := WORD_CP_1251_Cyrillic; - seGreek: lCodepage := WORD_CP_1253_Greek; - seTurkish: lCodepage := WORD_CP_1254_Turkish; - seHebrew: lCodepage := WORD_CP_1255_Hebrew; - seArabic: lCodepage := WORD_CP_1256_Arabic; + FCodePage := lowercase(ACodePage); + case FCodePage of + 'ucs2le': cp := WORD_UTF_16; // Biff 7 + 'cp437' : cp := WORD_CP_437_DOS_US; + 'cp850' : cp := WORD_CP_850_DOS_Latin1; + 'cp852' : cp := WORD_CP_852_DOS_Latin2; + 'cp866' : cp := WORD_CP_866_DOS_Cyrillic; + 'cp874' : cp := WORD_CP_874_Thai; + 'cp1250': cp := WORD_CP_1250_Latin2; + 'cp1251': cp := WORD_CP_1251_Cyrillic; + 'cp1252': cp := WORD_CP_1252_Latin1; + 'cp1253': cp := WORD_CP_1253_Greek; + 'cp1254': cp := WORD_CP_1254_Turkish; + 'cp1255': cp := WORD_CP_1255_Hebrew; + 'cp1256': cp := WORD_CP_1256_Arabic; + 'cp1257': cp := WORD_CP_1257_Baltic; + 'cp1258': cp := WORD_CP_1258_Vietnamese; else - // Default is Latin1 - lCodepage := WORD_CP_1252_Latin1; + Workbook.AddErrorMsg(rsCodePageNotSupported, [FCodePage]); + FCodePage := 'cp1252'; + cp := WORD_CP_1252_Latin1; end; - AStream.WriteWord(WordToLE(lCodepage)); + AStream.WriteWord(WordToLE(cp)); end; { Writes column info for the given column. Currently only the colum width is used. @@ -1958,7 +1966,8 @@ begin end; end; -{ Writes a NOTE record which describes a comment attached to a cell } +{ Writes a NOTE record which describes a comment attached to a cell + Valid für Biff2 and BIFF5} procedure TsSpreadBIFFWriter.WriteComment(AStream: TStream; ACell: PCell); const CHUNK_SIZE = 2048; @@ -1977,7 +1986,7 @@ begin List := TStringList.Create; try - List.Text := UTF8ToAnsi(ACell^.Comment); + List.Text := ConvertEncoding(ACell^.Comment, encodingUTF8, FCodePage); comment := List[0]; for p := 1 to List.Count-1 do comment := comment + #$0A + List[p]; @@ -2402,8 +2411,8 @@ begin shared formula RECORD here. The shared formula RECORD must follow the first FORMULA record referring to the shared formula} if (ACell^.SharedFormulaBase <> nil) and - (ARow = ACell^.SharedFormulaBase.Row) and - (ACol = ACell^.SharedFormulaBase.Col) + (ARow = ACell^.SharedFormulaBase^.Row) and + (ACol = ACell^.SharedFormulaBase^.Col) then WriteSharedFormula(AStream, ACell^.SharedFormulaBase); @@ -2484,8 +2493,8 @@ var begin rec.FormulaSize := WordToLE(5); rec.Token := INT_EXCEL_TOKEN_TEXP; // Marks the cell for using a shared formula - rec.Row := WordToLE(ACell^.SharedFormulaBase.Row); - rec.Col := WordToLE(ACell^.SharedFormulaBase.Col); + rec.Row := WordToLE(ACell^.SharedFormulaBase^.Row); + rec.Col := WordToLE(ACell^.SharedFormulaBase^.Col); AStream.WriteBuffer(rec, SizeOf(rec)); RPNLength := SizeOf(rec); end; @@ -2929,7 +2938,7 @@ var len: Byte; s: ansistring; begin - s := UTF8ToAnsi(AString); + s := ConvertEncoding(AString, encodingUTF8, FCodePage); len := Length(s); AStream.WriteByte(len); AStream.WriteBuffer(s[1], len); diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas index 7a9d835bc..facd893a9 100755 --- a/components/fpspreadsheet/xlsxooxml.pas +++ b/components/fpspreadsheet/xlsxooxml.pas @@ -2395,6 +2395,8 @@ end; procedure TsSpreadOOXMLWriter.WriteWorksheetRels(AWorksheet: TsWorksheet); begin + Unused(AWorksheet); + // Create stream SetLength(FSSheetRels, FCurSheetNum + 1); if (boBufStream in Workbook.Options) then