fpspreadsheet: Improve codepage handling for biff2 and biff2 files.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3924 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-02-04 18:15:19 +00:00
parent 028681029b
commit ac387f67e4
23 changed files with 283 additions and 221 deletions

View File

@ -321,7 +321,7 @@ begin
7: WorkbookSource.FileFormat := sfOpenDocument; // Open/LibreOffice 7: WorkbookSource.FileFormat := sfOpenDocument; // Open/LibreOffice
8: WorkbookSource.FileFormat := sfCSV; // Text files 8: WorkbookSource.FileFormat := sfCSV; // Text files
end; end;
WorkbookSource.FileName := AcFileOpen.Dialog.FileName; // this loads the file WorkbookSource.FileName := UTF8ToAnsi(AcFileOpen.Dialog.FileName); // this loads the file
UpdateCaption; UpdateCaption;
end; end;
@ -341,7 +341,7 @@ begin
6: fmt := sfCSV; 6: fmt := sfCSV;
7: fmt := sfWikiTable_WikiMedia; 7: fmt := sfWikiTable_WikiMedia;
end; end;
WorkbookSource.SaveToSpreadsheetFile(AcFileSaveAs.Dialog.FileName, fmt); WorkbookSource.SaveToSpreadsheetFile(UTF8ToAnsi(AcFileSaveAs.Dialog.FileName), fmt);
UpdateCaption; UpdateCaption;
finally finally
Screen.Cursor := crDefault; Screen.Cursor := crDefault;
@ -401,7 +401,7 @@ begin
Caption := 'demo_ctrls' Caption := 'demo_ctrls'
else else
Caption := Format('demo_ctrls - "%s" [%s]', [ Caption := Format('demo_ctrls - "%s" [%s]', [
WorkbookSource.Filename, AnsiToUTF8(WorkbookSource.Filename),
GetFileFormatName(WorkbookSource.Workbook.FileFormat) GetFileFormatName(WorkbookSource.Workbook.FileFormat)
]); ]);
end; end;

View File

@ -120,6 +120,8 @@ implementation
{ TForm1 } { TForm1 }
procedure TForm1.BtnLoadClick(Sender: TObject); procedure TForm1.BtnLoadClick(Sender: TObject);
var
fn: ansistring;
begin begin
if OpenDialog.Execute then begin if OpenDialog.Execute then begin
WorkbookSource.AutodetectFormat := false; WorkbookSource.AutodetectFormat := false;
@ -136,16 +138,17 @@ begin
end; end;
// There are 3 possibilities to open a file: // There are 3 possibilities to open a file:
fn := UTF8ToAnsi(OpenDialog.Filename);
case CbLoader.ItemIndex of case CbLoader.ItemIndex of
0: if WorkbookSource.AutodetectFormat then 0: if WorkbookSource.AutodetectFormat then
WorkbookSource.Workbook.ReadFromFile(OpenDialog.FileName) WorkbookSource.Workbook.ReadFromFile(fn)
else else
WorkbookSource.Workbook.ReadFromFile(OpenDialog.Filename, WorkbookSource.FileFormat); WorkbookSource.Workbook.ReadFromFile(fn, WorkbookSource.FileFormat);
1: WorkbookSource.FileName := OpenDialog.FileName; // this loads the file 1: WorkbookSource.FileName := fn; // this loads the file
2: if WorkbookSource.AutodetectFormat then 2: if WorkbookSource.AutodetectFormat then
WorksheetGrid.LoadFromSpreadsheetFile(OpenDialog.FileName) WorksheetGrid.LoadFromSpreadsheetFile(fn)
else else
WorksheetGrid.LoadFromSpreadsheetFile(OpenDialog.FileName, WorkbookSource.FileFormat); WorksheetGrid.LoadFromSpreadsheetFile(fn, WorkbookSource.FileFormat);
end; end;
end; end;
end; end;

View File

@ -115,21 +115,22 @@ end;
// Saves sheet in grid to file, overwriting existing file // Saves sheet in grid to file, overwriting existing file
procedure TForm1.BtnSaveClick(Sender: TObject); procedure TForm1.BtnSaveClick(Sender: TObject);
var var
err: String; err, fn: String;
begin begin
if WorksheetGrid.Workbook = nil then if WorksheetGrid.Workbook = nil then
exit; exit;
if WorksheetGrid.Workbook.Filename <>'' then begin if WorksheetGrid.Workbook.Filename <>'' then begin
SaveDialog.InitialDir := ExtractFileDir(WorksheetGrid.Workbook.FileName); fn := AnsiToUTF8(WorksheetGrid.Workbook.Filename);
SaveDialog.FileName := ChangeFileExt(ExtractFileName(WorksheetGrid.Workbook.FileName), ''); SaveDialog.InitialDir := ExtractFileDir(fn);
SaveDialog.FileName := ChangeFileExt(ExtractFileName(fn), '');
end; end;
if SaveDialog.Execute then if SaveDialog.Execute then
begin begin
Screen.Cursor := crHourglass; Screen.Cursor := crHourglass;
try try
WorksheetGrid.SaveToSpreadsheetFile(SaveDialog.FileName); WorksheetGrid.SaveToSpreadsheetFile(UTF8ToAnsi(SaveDialog.FileName));
finally finally
Screen.Cursor := crDefault; Screen.Cursor := crDefault;
// Show a message in case of error(s) // Show a message in case of error(s)

View File

@ -7,7 +7,7 @@ uses
cthreads, cthreads,
{$ENDIF}{$ENDIF} {$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset Interfaces, // this includes the LCL widgetset
Forms, mainfrm, fpsHelpers Forms, mainfrm, fpsCell
{ you can add units after this }; { you can add units after this };
{$R *.res} {$R *.res}

View File

@ -92,21 +92,23 @@ end;
procedure TForm1.BtnSaveClick(Sender: TObject); procedure TForm1.BtnSaveClick(Sender: TObject);
var var
err: String; err: String;
fn: String;
begin begin
if Grid.Workbook = nil then if Grid.Workbook = nil then
exit; exit;
if Grid.Workbook.Filename <>'' then if Grid.Workbook.Filename <>'' then
begin begin
SaveDialog.InitialDir := ExtractFileDir(Grid.Workbook.FileName); fn := AnsiToUtf8(Grid.Workbook.Filename);
SaveDialog.FileName := ChangeFileExt(ExtractFileName(Grid.Workbook.FileName), ''); SaveDialog.InitialDir := ExtractFileDir(fn);
SaveDialog.FileName := ChangeFileExt(ExtractFileName(fn), '');
end; end;
if SaveDialog.Execute then if SaveDialog.Execute then
begin begin
Screen.Cursor := crHourglass; Screen.Cursor := crHourglass;
try try
Grid.SaveToSpreadsheetFile(SaveDialog.FileName); Grid.SaveToSpreadsheetFile(UTF8ToAnsi(SaveDialog.FileName));
finally finally
Screen.Cursor := crDefault; Screen.Cursor := crDefault;
// Show a message in case of error(s) // Show a message in case of error(s)

View File

@ -498,7 +498,7 @@ object MainFrm: TMainFrm
BorderStyle = bsNone BorderStyle = bsNone
ColCount = 27 ColCount = 27
MouseWheelOption = mwGrid 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 RowCount = 101
TabOrder = 1 TabOrder = 1
TitleStyle = tsNative TitleStyle = tsNative

View File

@ -324,6 +324,7 @@ type
procedure WorksheetGridHeaderClick(Sender: TObject; IsColumn: Boolean; procedure WorksheetGridHeaderClick(Sender: TObject; IsColumn: Boolean;
Index: Integer); Index: Integer);
procedure WorksheetGridSelection(Sender: TObject; aCol, aRow: Integer); procedure WorksheetGridSelection(Sender: TObject; aCol, aRow: Integer);
private private
FCopiedFormat: TCell; FCopiedFormat: TCell;
procedure LoadFile(const AFileName: String); procedure LoadFile(const AFileName: String);
@ -759,7 +760,7 @@ begin
7: fmt := sfWikiTable_wikimedia; 7: fmt := sfWikiTable_wikimedia;
end; end;
try try
WorksheetGrid.SaveToSpreadsheetFile(SaveDialog.FileName, fmt); WorksheetGrid.SaveToSpreadsheetFile(Utf8ToAnsi(SaveDialog.FileName), fmt);
finally finally
Screen.Cursor := crDefault; Screen.Cursor := crDefault;
err := WorksheetGrid.Workbook.ErrorMsg; err := WorksheetGrid.Workbook.ErrorMsg;
@ -1006,7 +1007,7 @@ begin
end; end;
procedure TMainFrm.LoadFile(const AFileName: String); 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 var
err: String; err: String;
begin begin
@ -1014,7 +1015,7 @@ begin
Screen.Cursor := crHourglass; Screen.Cursor := crHourglass;
try try
try try
WorksheetGrid.LoadFromSpreadsheetFile(UTF8ToSys(AFileName)); WorksheetGrid.LoadFromSpreadsheetFile(utf8ToAnsi(AFileName));
except except
on E: Exception do begin on E: Exception do begin
// In an error occurs show at least an empty valid worksheet // In an error occurs show at least an empty valid worksheet

View File

@ -1248,9 +1248,9 @@ procedure TsCellCommentAction.ExecuteTarget(Target: TObject);
var var
txt: String; txt: String;
cellStr: String; cellStr: String;
x, y: Integer;
R: TRect;
begin begin
Unused(Target);
if Worksheet = nil then if Worksheet = nil then
exit; exit;
@ -1275,6 +1275,8 @@ end;
procedure TsCellCommentAction.UpdateTarget(Target: TObject); procedure TsCellCommentAction.UpdateTarget(Target: TObject);
begin begin
Unused(Target);
case FMode of case FMode of
ccmNew : Enabled := (Worksheet <> nil) and (Length(GetSelection) > 0); ccmNew : Enabled := (Worksheet <> nil) and (Length(GetSelection) > 0);
ccmEdit, ccmEdit,

View File

@ -65,7 +65,7 @@ type
LineEnding: TsCSVLineEnding; // W: Specification for line ending to be written LineEnding: TsCSVLineEnding; // W: Specification for line ending to be written
Delimiter: Char; // RW: Column delimiter Delimiter: Char; // RW: Column delimiter
QuoteChar: Char; // RW: Character for quoting texts 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 DetectContentType: Boolean; // R: try to convert strings to content types
NumberFormat: String; // W: if empty write numbers like in sheet, otherwise use this format 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 AutoDetectNumberFormat: Boolean; // R: automatically detects decimal/thousand separator used in numbers
@ -202,8 +202,6 @@ end;
constructor TsCSVReader.Create(AWorkbook: TsWorkbook); constructor TsCSVReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FFormatSettings := CSVParams.FormatSettings;
ReplaceFormatSettings(FFormatSettings, AWorkbook.FormatSettings);
FWorksheetName := 'Sheet1'; // will be replaced by filename FWorksheetName := 'Sheet1'; // will be replaced by filename
end; end;
@ -455,6 +453,9 @@ begin
FWorkbook := AData; FWorkbook := AData;
FWorksheet := AData.AddWorksheet(FWorksheetName, true); FWorksheet := AData.AddWorksheet(FWorksheetName, true);
FFormatSettings := CSVParams.FormatSettings;
ReplaceFormatSettings(FFormatSettings, FWorkbook.FormatSettings);
// Create csv parser, read file and store in worksheet // Create csv parser, read file and store in worksheet
Parser := TCSVParser.Create; Parser := TCSVParser.Create;
try try

View File

@ -59,9 +59,9 @@ type
MergeBase: PCell; // Upper left cell of a merged range MergeBase: PCell; // Upper left cell of a merged range
Comment: String; // Comment attached to the cell Comment: String; // Comment attached to the cell
{ Cell content } { Cell content }
UTF8StringValue: String; // strings cannot be part of a variant record UTF8StringValue: String; // Strings cannot be part of a variant record
FormulaValue: String; 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 cctEmpty : (); // has no data at all
cctFormula : (); // FormulaValue is outside the variant record cctFormula : (); // FormulaValue is outside the variant record
cctNumber : (Numbervalue: Double); cctNumber : (Numbervalue: Double);
@ -187,9 +187,9 @@ type
procedure UpdateCaches; procedure UpdateCaches;
{ Reading of values } { Reading of values }
function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload; function ReadAsUTF8Text(ARow, ACol: Cardinal): string; overload; //ansistring; overload;
function ReadAsUTF8Text(ACell: PCell): ansistring; overload; function ReadAsUTF8Text(ACell: PCell): string; overload; //ansistring; overload;
function ReadAsUTF8Text(ACell: PCell; AFormatSettings: TFormatSettings): ansistring; overload; function ReadAsUTF8Text(ACell: PCell; AFormatSettings: TFormatSettings): string; overload; //ansistring; overload;
function ReadAsNumber(ARow, ACol: Cardinal): Double; overload; function ReadAsNumber(ARow, ACol: Cardinal): Double; overload;
function ReadAsNumber(ACell: PCell): Double; overload; function ReadAsNumber(ACell: PCell): Double; overload;
function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload; function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload;
@ -555,7 +555,8 @@ type
private private
{ Internal data } { Internal data }
FWorksheets: TFPList; FWorksheets: TFPList;
FEncoding: TsEncoding; FCodePage: String;
// FEncoding: TsEncoding;
FFormat: TsSpreadsheetFormat; FFormat: TsSpreadsheetFormat;
FBuiltinFontCount: Integer; FBuiltinFontCount: Integer;
FPalette: array of TsColorValue; FPalette: array of TsColorValue;
@ -694,7 +695,8 @@ type
property ActiveWorksheet: TsWorksheet read FActiveWorksheet; property ActiveWorksheet: TsWorksheet read FActiveWorksheet;
{@@ This property is only used for formats which don't support unicode {@@ 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 } 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 } {@@ Retrieves error messages collected during reading/writing }
property ErrorMsg: String read GetErrorMsg; property ErrorMsg: String read GetErrorMsg;
{@@ Filename of the saved workbook } {@@ Filename of the saved workbook }
@ -2446,13 +2448,13 @@ end;
Reads the contents of a cell and returns an user readable text Reads the contents of a cell and returns an user readable text
representing the contents of the cell. 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 ARow The row of the cell
@param ACol The column of the cell @param ACol The column of the cell
@return The text representation 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 begin
Result := ReadAsUTF8Text(GetCell(ARow, ACol)); Result := ReadAsUTF8Text(GetCell(ARow, ACol));
end; end;
@ -2461,21 +2463,21 @@ end;
Reads the contents of a cell and returns an user readable text Reads the contents of a cell and returns an user readable text
representing the contents of the cell. 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 @param ACell Pointer to the cell
@return The text representation of the cell @return The text representation of the cell
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; function TsWorksheet.ReadAsUTF8Text(ACell: PCell): string; //ansistring;
begin begin
Result := ReadAsUTF8Text(ACell, FWorkbook.FormatSettings); Result := ReadAsUTF8Text(ACell, FWorkbook.FormatSettings);
end; end;
function TsWorksheet.ReadAsUTF8Text(ACell: PCell; function TsWorksheet.ReadAsUTF8Text(ACell: PCell;
AFormatSettings: TFormatSettings): ansistring; AFormatSettings: TFormatSettings): string; //ansistring;
function FloatToStrNoNaN(const AValue: Double; function FloatToStrNoNaN(const AValue: Double;
ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring; ANumberFormat: TsNumberFormat; ANumberFormatStr: string): string; //ansistring;
begin begin
if IsNan(AValue) then if IsNan(AValue) then
Result := '' Result := ''
@ -2493,7 +2495,7 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell;
end; end;
function DateTimeToStrNoNaN(const Value: Double; function DateTimeToStrNoNaN(const Value: Double;
ANumberFormat: TsNumberFormat; ANumberFormatStr: String): ansistring; ANumberFormat: TsNumberFormat; ANumberFormatStr: String): string; //ansistring;
var var
fmtp, fmtn, fmt0: String; fmtp, fmtn, fmt0: String;
begin begin
@ -6424,11 +6426,14 @@ end;
constructor TsWorkbook.Create; constructor TsWorkbook.Create;
var var
fmt: TsCellFormat; fmt: TsCellFormat;
cp: String;
begin begin
inherited Create; inherited Create;
FWorksheets := TFPList.Create; FWorksheets := TFPList.Create;
FLog := TStringList.Create; FLog := TStringList.Create;
FFormat := sfExcel8; FFormat := sfExcel8;
FCodePage := GetDefaultTextEncoding;
// FEncoding := seLatin1;
FormatSettings := UTF8FormatSettings; FormatSettings := UTF8FormatSettings;
FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat); FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat);
@ -8736,6 +8741,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsCustomSpreadWriter.WriteComment(AStream: TStream; ACell: PCell); procedure TsCustomSpreadWriter.WriteComment(AStream: TStream; ACell: PCell);
begin begin
Unused(AStream, ACell);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------

View File

@ -1845,7 +1845,6 @@ var
lCell: PCell; lCell: PCell;
justif: Byte; justif: Byte;
fmt: PsCellFormat; fmt: PsCellFormat;
savedBrushColor: TColor;
begin begin
if (Worksheet = nil) then if (Worksheet = nil) then
exit; exit;

View File

@ -10,7 +10,7 @@
unit fpsRPN; unit fpsRPN;
{$ifdef fpc} {$ifdef fpc}
{$mode delphi}{$H+} {$mode objfpc}{$H+}
{$endif} {$endif}
interface interface
@ -436,6 +436,7 @@ begin
while item <> nil do begin while item <> nil do begin
nextitem := item^.Next; nextitem := item^.Next;
Result[n] := item^.FE; Result[n] := item^.FE;
Result[n].StringValue := item^.FE.StringValue;
if AReverse then dec(n) else inc(n); if AReverse then dec(n) else inc(n);
DisposeRPNItem(item); DisposeRPNItem(item);
item := nextitem; item := nextitem;

View File

@ -52,6 +52,7 @@ resourcestring
'text found in cell %s.'; 'text found in cell %s.';
rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).'; rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).';
rsAmbiguousDecThouSeparator = 'Assuming usage of decimal separator in "%s".'; 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? rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings?

View File

@ -48,7 +48,7 @@ const
type type
(*
{@@ Possible encodings for a non-unicode encoded text } {@@ Possible encodings for a non-unicode encoded text }
TsEncoding = ( TsEncoding = (
seLatin1, seLatin1,
@ -57,8 +57,9 @@ type
seGreek, seGreek,
seTurkish, seTurkish,
seHebrew, seHebrew,
seArabic seArabic,
); seUTF16
); *)
{@@ Tokens to identify the <b>elements in an expanded formula</b>. {@@ Tokens to identify the <b>elements in an expanded formula</b>.
@ -110,7 +111,7 @@ type
ElementKind: TFEKind; ElementKind: TFEKind;
Row, Row2: Cardinal; // zero-based Row, Row2: Cardinal; // zero-based
Col, Col2: Cardinal; // zero-based Col, Col2: Cardinal; // zero-based
Param1, Param2: Word; // Extra parameters // Param1, Param2: Word; // Extra parameters
DoubleValue: double; DoubleValue: double;
IntValue: Word; IntValue: Word;
StringValue: String; StringValue: String;
@ -228,7 +229,7 @@ type
Due to limitations of the text mode the characters are not rotated here. Due to limitations of the text mode the characters are not rotated here.
There is, however, also a "stacked" variant which looks exactly like There is, however, also a "stacked" variant which looks exactly like
the former case. the 90-degrees-clockwise case.
} }
TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation, TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation,
rt90DegreeCounterClockwiseRotation, rtStacked); rt90DegreeCounterClockwiseRotation, rtStacked);

View File

@ -228,7 +228,7 @@ begin
SetLength(sa, ls); SetLength(sa, ls);
ANumbytes := ls*SizeOf(AnsiChar) + ALenBytes + 1; ANumbytes := ls*SizeOf(AnsiChar) + ALenBytes + 1;
Move(FBuffer[ABufIndex + ALenBytes + 1], sa[1], ls*SizeOf(AnsiChar)); Move(FBuffer[ABufIndex + ALenBytes + 1], sa[1], ls*SizeOf(AnsiChar));
AString := sa; AString := AnsiToUTF8(sa);
end else begin end else begin
SetLength(sw, ls); SetLength(sw, ls);
ANumBytes := ls*SizeOf(WideChar) + ALenBytes + 1; ANumBytes := ls*SizeOf(WideChar) + ALenBytes + 1;

View File

@ -381,7 +381,9 @@ var
ActualDateTime: TDateTime; ActualDateTime: TDateTime;
Row: Cardinal; Row: Cardinal;
TempFile: string; //write xls/xml to this file and read back from it TempFile: string; //write xls/xml to this file and read back from it
ErrorMargin: TDateTime;
begin begin
ErrorMargin := 1.0/(24*60*60*1000*100); // 0.01 ms
TempFile:=NewTempFile; TempFile:=NewTempFile;
{// Not needed: use workbook.writetofile with overwrite=true {// Not needed: use workbook.writetofile with overwrite=true
if fileexists(TempFile) then if fileexists(TempFile) then
@ -397,7 +399,8 @@ begin
// Some checks inside worksheet itself // Some checks inside worksheet itself
if not(MyWorkSheet.ReadAsDateTime(Row,0,ActualDateTime)) then if not(MyWorkSheet.ReadAsDateTime(Row,0,ActualDateTime)) then
Fail('Failed writing date time for cell '+CellNotation(MyWorkSheet,Row)); 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; end;
MyWorkBook.WriteToFile(TempFile, AFormat, true); MyWorkBook.WriteToFile(TempFile, AFormat, true);
finally finally
@ -420,7 +423,8 @@ begin
begin begin
if not(MyWorkSheet.ReadAsDateTime(Row,0,ActualDateTime)) then if not(MyWorkSheet.ReadAsDateTime(Row,0,ActualDateTime)) then
Fail('Could not read date time for cell '+CellNotation(MyWorkSheet,Row)); 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; end;
finally finally
MyWorkbook.Free; MyWorkbook.Free;

View File

@ -48,6 +48,7 @@
<Unit1> <Unit1>
<Filename Value="datetests.pas"/> <Filename Value="datetests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="datetests"/>
</Unit1> </Unit1>
<Unit2> <Unit2>
<Filename Value="stringtests.pas"/> <Filename Value="stringtests.pas"/>
@ -60,7 +61,6 @@
<Unit4> <Unit4>
<Filename Value="manualtests.pas"/> <Filename Value="manualtests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="manualtests"/>
</Unit4> </Unit4>
<Unit5> <Unit5>
<Filename Value="testsutility.pas"/> <Filename Value="testsutility.pas"/>
@ -69,21 +69,19 @@
<Unit6> <Unit6>
<Filename Value="internaltests.pas"/> <Filename Value="internaltests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="internaltests"/>
</Unit6> </Unit6>
<Unit7> <Unit7>
<Filename Value="formattests.pas"/> <Filename Value="formattests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="formattests"/>
</Unit7> </Unit7>
<Unit8> <Unit8>
<Filename Value="colortests.pas"/> <Filename Value="colortests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="colortests"/>
</Unit8> </Unit8>
<Unit9> <Unit9>
<Filename Value="fonttests.pas"/> <Filename Value="fonttests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="fonttests"/>
</Unit9> </Unit9>
<Unit10> <Unit10>
<Filename Value="optiontests.pas"/> <Filename Value="optiontests.pas"/>
@ -96,15 +94,16 @@
<Unit12> <Unit12>
<Filename Value="rpnformulaunit.pas"/> <Filename Value="rpnformulaunit.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="rpnFormulaUnit"/>
</Unit12> </Unit12>
<Unit13> <Unit13>
<Filename Value="formulatests.pas"/> <Filename Value="formulatests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="formulatests"/>
</Unit13> </Unit13>
<Unit14> <Unit14>
<Filename Value="emptycelltests.pas"/> <Filename Value="emptycelltests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="emptycelltests"/>
</Unit14> </Unit14>
<Unit15> <Unit15>
<Filename Value="errortests.pas"/> <Filename Value="errortests.pas"/>
@ -121,7 +120,6 @@
<Unit18> <Unit18>
<Filename Value="celltypetests.pas"/> <Filename Value="celltypetests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="celltypetests"/>
</Unit18> </Unit18>
<Unit19> <Unit19>
<Filename Value="sortingtests.pas"/> <Filename Value="sortingtests.pas"/>
@ -130,7 +128,6 @@
<Unit20> <Unit20>
<Filename Value="copytests.pas"/> <Filename Value="copytests.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="copytests"/>
</Unit20> </Unit20>
</Units> </Units>
</ProjectOptions> </ProjectOptions>

View File

@ -52,7 +52,7 @@
{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! {!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT CHANGE THIS FORMULA - ITS RESULT (-9) IS HARD-CODED IN OTHER TESTS !! !! DO NOT CHANGE THIS FORMULA - ITS RESULT (-9) IS HARD-CODED IN OTHER TESTS !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!} !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}
(*
// Add cell values - relative addresses // Add cell values - relative addresses
inc(Row); inc(Row);
formula := 'B1+B2'; formula := 'B1+B2';
@ -3001,7 +3001,7 @@
SetLength(sollValues, Row+1); SetLength(sollValues, Row+1);
sollValues[Row] := StringResult('Ha'); sollValues[Row] := StringResult('Ha');
Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString); Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
*)
// LEFT (2 parameters, utf8) // LEFT (2 parameters, utf8)
inc(Row); inc(Row);
formula := 'LEFT("Ändern",3)'; formula := 'LEFT("Ändern",3)';

View File

@ -57,7 +57,7 @@ type
TsSpreadBIFF2Reader = class(TsSpreadBIFFReader) TsSpreadBIFF2Reader = class(TsSpreadBIFFReader)
private private
WorkBookEncoding: TsEncoding; // WorkBookEncoding: TsEncoding;
FFont: TsFont; FFont: TsFont;
protected protected
procedure CreateNumFormatList; override; procedure CreateNumFormatList; override;
@ -110,6 +110,8 @@ type
ACell: PCell); override; ACell: PCell); override;
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override; 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; procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TsErrorValue; ACell: PCell); override; const AValue: TsErrorValue; ACell: PCell); override;
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
@ -512,7 +514,7 @@ begin
FWorkbook.RemoveAllFonts; FWorkbook.RemoveAllFonts;
{ Store some data about the workbook that other routines need } { Store some data about the workbook that other routines need }
WorkBookEncoding := AData.Encoding; //WorkBookEncoding := AData.Encoding;
BIFF2EOF := False; BIFF2EOF := False;
@ -531,6 +533,7 @@ begin
case RecordType of case RecordType of
INT_EXCEL_ID_BLANK : ReadBlank(AStream); INT_EXCEL_ID_BLANK : ReadBlank(AStream);
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream); INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
INT_EXCEL_ID_NOTE : ReadComment(AStream); INT_EXCEL_ID_NOTE : ReadComment(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream); INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream); INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream);
@ -648,8 +651,8 @@ var
L: Byte; L: Byte;
ARow, ACol: Cardinal; ARow, ACol: Cardinal;
XF: Word; XF: Word;
AValue: ansistring; ansiStr: ansistring;
AStrValue: UTF8String; valueStr: UTF8String;
cell: PCell; cell: PCell;
begin begin
{ Read entire record, starting at Row, except for string data } { Read entire record, starting at Row, except for string data }
@ -661,10 +664,12 @@ begin
{ String with 8-bit size } { String with 8-bit size }
L := rec.TextLen; L := rec.TextLen;
SetLength(AValue, L); SetLength(ansiStr, L);
AStream.ReadBuffer(AValue[1], L); AStream.ReadBuffer(ansiStr[1], L);
{ Save the data } { Save the data }
valueStr := ConvertEncoding(ansiStr, FCodePage, encodingUTF8);
{
case WorkBookEncoding of case WorkBookEncoding of
seLatin2: AStrValue := CP1250ToUTF8(AValue); seLatin2: AStrValue := CP1250ToUTF8(AValue);
seCyrillic: AStrValue := CP1251ToUTF8(AValue); seCyrillic: AStrValue := CP1251ToUTF8(AValue);
@ -676,6 +681,7 @@ begin
// Latin 1 is the default // Latin 1 is the default
AStrValue := CP1252ToUTF8(AValue); AStrValue := CP1252ToUTF8(AValue);
end; end;
}
{ Create cell } { Create cell }
if FIsVirtualMode then begin if FIsVirtualMode then begin
@ -683,7 +689,7 @@ begin
cell := @FVirtualCell; cell := @FVirtualCell;
end else end else
cell := FWorksheet.GetCell(ARow, ACol); cell := FWorksheet.GetCell(ARow, ACol);
FWorksheet.WriteUTF8Text(cell, AStrValue); FWorksheet.WriteUTF8Text(cell, valueStr);
{ Apply formatting to cell } { Apply formatting to cell }
ApplyCellFormatting(cell, XF); ApplyCellFormatting(cell, XF);
@ -870,7 +876,8 @@ begin
begin begin
// The "IncompleteCell" has been identified in the sheet when reading // The "IncompleteCell" has been identified in the sheet when reading
// the FORMULA record which precedes the String record. // 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; FIncompleteCell^.ContentType := cctUTF8String;
if FIsVirtualMode then if FIsVirtualMode then
Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell); Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell);
@ -1141,6 +1148,32 @@ begin
AStream.WriteBuffer(rec, SizeOf(rec)); AStream.WriteBuffer(rec, SizeOf(rec));
end; 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 Writes an Excel 2 COLWIDTH record
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -1245,6 +1278,7 @@ begin
WriteBOF(AStream); WriteBOF(AStream);
WriteFonts(AStream); WriteFonts(AStream);
WriteCodePage(AStream, Workbook.CodePage); //Encoding);
WriteFormatCount(AStream); WriteFormatCount(AStream);
WriteNumFormats(AStream); WriteNumFormats(AStream);
WriteXFRecords(AStream); WriteXFRecords(AStream);
@ -1531,7 +1565,7 @@ var
begin begin
Unused(ANumFormatData); Unused(ANumFormatData);
s := NumFormatList.FormatStringForWriting(AListIndex); s := ConvertEncoding(NumFormatList.FormatStringForWriting(AListIndex), encodingUTF8, FCodePage);
len := Length(s); len := Length(s);
{ BIFF record header } { BIFF record header }

View File

@ -34,19 +34,15 @@ EOF
The row and column numbering in BIFF files is zero-based. The row and column numbering in BIFF files is zero-based.
Excel file format specification obtained from: 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: 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: 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 Encoding information: as specified fy the Encoding property of the workbook.
other characters, please use a format which support unicode
AUTHORS: Felipe Monteiro de Carvalho AUTHORS: Felipe Monteiro de Carvalho
} }
@ -101,8 +97,6 @@ type
{ TsSpreadBIFF5Writer } { TsSpreadBIFF5Writer }
TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter) TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter)
private
WorkBookEncoding: TsEncoding;
protected protected
{ Record writing methods } { Record writing methods }
procedure WriteBOF(AStream: TStream; ADataType: Word); procedure WriteBOF(AStream: TStream; ADataType: Word);
@ -353,6 +347,7 @@ begin
case RecordType of case RecordType of
INT_EXCEL_ID_BOF : ; INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream); INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream); INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FORMAT : ReadFormat(AStream); INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
INT_EXCEL_ID_XF : ReadXF(AStream); INT_EXCEL_ID_XF : ReadXF(AStream);
@ -481,7 +476,8 @@ begin
SetLength(s, Len); SetLength(s, Len);
AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar)); AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar));
sheetName := AnsiToUTF8(s); // sheetName := AnsiToUTF8(s);
sheetName := ConvertEncoding(s, FCodePage, EncodingUTF8);
FWorksheetNames.Add(sheetName); FWorksheetNames.Add(sheetName);
end; end;
@ -550,7 +546,8 @@ begin
SetLength(s, Len); SetLength(s, Len);
AStream.ReadBuffer(s[1], len); AStream.ReadBuffer(s[1], len);
if (FIncompleteCell <> nil) and (s <> '') then begin if (FIncompleteCell <> nil) and (s <> '') then begin
FIncompleteCell^.UTF8StringValue := AnsiToUTF8(s); // FIncompleteCell^.UTF8StringValue := AnsiToUTF8(s);
FIncompletecell^.UTF8StringValue := ConvertEncoding(s, FCodePage, encodingUTF8);
FIncompleteCell^.ContentType := cctUTF8String; FIncompleteCell^.ContentType := cctUTF8String;
if FIsVirtualMode then if FIsVirtualMode then
Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell); Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell);
@ -720,7 +717,6 @@ end;
procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook); procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var var
BIFF5EOF: Boolean; BIFF5EOF: Boolean;
p,s: Int64;
begin begin
{ Initializations } { Initializations }
@ -743,8 +739,6 @@ begin
ReadWorksheet(AStream, AData); ReadWorksheet(AStream, AData);
// Check for the end of the file // Check for the end of the file
p := AStream.Position;
s := AStream.Size;
if AStream.Position >= AStream.Size then BIFF5EOF := True; if AStream.Position >= AStream.Size then BIFF5EOF := True;
// Final preparations // Final preparations
@ -818,7 +812,7 @@ begin
Len := AStream.ReadByte(); Len := AStream.ReadByte();
SetLength(fontname, Len); SetLength(fontname, Len);
AStream.ReadBuffer(fontname[1], Len); AStream.ReadBuffer(fontname[1], Len);
font.FontName := fontname; font.FontName := ConvertEncoding(fontname, FCodePage, encodingUTF8);
{ Add font to workbook's font list } { Add font to workbook's font list }
FWorkbook.AddFont(font); FWorkbook.AddFont(font);
@ -846,7 +840,8 @@ begin
AStream.ReadBuffer(fmtString[1], len); AStream.ReadBuffer(fmtString[1], len);
// Add to the list // Add to the list
NumFormatList.AnalyzeAndAdd(fmtIndex, AnsiToUTF8(fmtString)); // NumFormatList.AnalyzeAndAdd(fmtIndex, AnsiToUTF8(fmtString));
NumFormatList.AnalyzeAndAdd(fmtIndex, ConvertEncoding(fmtString, FCodePage, encodingUTF8));
end; end;
procedure TsSpreadBIFF5Reader.ReadLabel(AStream: TStream); procedure TsSpreadBIFF5Reader.ReadLabel(AStream: TStream);
@ -856,7 +851,8 @@ var
ARow, ACol: Cardinal; ARow, ACol: Cardinal;
XF: WORD; XF: WORD;
cell: PCell; cell: PCell;
AValue: ansistring; ansistr: ansistring;
valuestr: String;
begin begin
rec.Row := 0; // to silence the compiler... rec.Row := 0; // to silence the compiler...
@ -868,8 +864,8 @@ begin
{ Byte String with 16-bit size } { Byte String with 16-bit size }
L := WordLEToN(rec.TextLen); L := WordLEToN(rec.TextLen);
SetLength(AValue, L); SetLength(ansistr, L);
AStream.ReadBuffer(AValue[1], L); AStream.ReadBuffer(ansistr[1], L);
{ Create cell } { Create cell }
if FIsVirtualMode then begin if FIsVirtualMode then begin
@ -879,7 +875,8 @@ begin
cell := FWorksheet.GetCell(ARow, ACol); cell := FWorksheet.GetCell(ARow, ACol);
{ Save the data } { 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 } { Add attributes }
ApplyCellFormatting(cell, XF); ApplyCellFormatting(cell, XF);
@ -939,14 +936,11 @@ var
i, len: Integer; i, len: Integer;
pane: Byte; pane: Byte;
begin begin
{ Store some data about the workbook that other routines need }
WorkBookEncoding := Workbook.Encoding;
{ Write workbook globals } { Write workbook globals }
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
WriteCodepage(AStream, WorkBookEncoding); WriteCodepage(AStream, Workbook.CodePage); //WorkBook.Encoding);
WriteWindow1(AStream); WriteWindow1(AStream);
WriteFonts(AStream); WriteFonts(AStream);
WriteNumFormats(AStream); WriteNumFormats(AStream);
@ -1042,11 +1036,14 @@ end;
function TsSpreadBIFF5Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; function TsSpreadBIFF5Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
var var
Len: Byte; Len: Byte;
LatinSheetName: string; xlsSheetName: ansistring;
begin begin
xlsSheetName := ConvertEncoding(ASheetName, encodingUTF8, FCodePage);
Len := Length(xlsSheetName);
{
LatinSheetName := UTF8ToISO_8859_1(ASheetName); LatinSheetName := UTF8ToISO_8859_1(ASheetName);
Len := Length(LatinSheetName); Len := Length(LatinSheetName);
}
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOUNDSHEET)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOUNDSHEET));
AStream.WriteWord(WordToLE(6 + 1 + Len)); AStream.WriteWord(WordToLE(6 + 1 + Len));
@ -1064,7 +1061,8 @@ begin
{ Sheet name: Byte string, 8-bit length } { Sheet name: Byte string, 8-bit length }
AStream.WriteByte(Len); AStream.WriteByte(Len);
AStream.WriteBuffer(LatinSheetName[1], Len); // AStream.WriteBuffer(LatinSheetName[1], Len);
AStream.WriteBuffer(xlsSheetName[1], Len);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -1204,15 +1202,18 @@ type
end; end;
var var
len: Integer; len: Integer;
s: ansistring; fmtStr: String;
ansiFmtStr: ansiString;
rec: TNumFormatRecord; rec: TNumFormatRecord;
buf: array of byte; buf: array of byte;
begin begin
if (ANumFormatData = nil) or (ANumFormatData.FormatString = '') then if (ANumFormatData = nil) or (ANumFormatData.FormatString = '') then
exit; exit;
s := UTF8ToAnsi(NumFormatList.FormatStringForWriting(AListIndex)); // s := UTF8ToAnsi(NumFormatList.FormatStringForWriting(AListIndex));
len := Length(s); fmtStr := NumFormatList.FormatStringForWriting(AListIndex);
ansiFmtStr := ConvertEncoding(fmtStr, encodingUTF8, FCodePage);
len := Length(ansiFmtStr);
{ BIFF record header } { BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_FORMAT); rec.RecordID := WordToLE(INT_EXCEL_ID_FORMAT);
@ -1227,7 +1228,7 @@ begin
{ Copy the format string characters into a buffer immediately after rec } { Copy the format string characters into a buffer immediately after rec }
SetLength(buf, SizeOf(rec) + SizeOf(ansiChar)*len); SetLength(buf, SizeOf(rec) + SizeOf(ansiChar)*len);
Move(rec, buf[0], SizeOf(rec)); 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 } { Write out }
AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*len); AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*len);
@ -1284,6 +1285,8 @@ begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit; exit;
ansiValue := ConvertEncoding(AValue, encodingUTF8, FCodePage);
{
case WorkBookEncoding of case WorkBookEncoding of
seLatin2: AnsiValue := UTF8ToCP1250(AValue); seLatin2: AnsiValue := UTF8ToCP1250(AValue);
seCyrillic: AnsiValue := UTF8ToCP1251(AValue); seCyrillic: AnsiValue := UTF8ToCP1251(AValue);
@ -1295,6 +1298,7 @@ begin
// Latin 1 is the default // Latin 1 is the default
AnsiValue := UTF8ToCP1252(AValue); AnsiValue := UTF8ToCP1252(AValue);
end; end;
}
if AnsiValue = '' then begin if AnsiValue = '' then begin
// Bad formatted UTF8String (maybe ANSI?) // Bad formatted UTF8String (maybe ANSI?)
@ -1354,7 +1358,8 @@ var
s: ansistring; s: ansistring;
len: Integer; len: Integer;
begin begin
s := UTF8ToAnsi(AString); // s := UTF8ToAnsi(AString);
s := ConvertEncoding(AString, encodingUTF8, FCodePage);
len := Length(s); len := Length(s);
{ BIFF Record header } { BIFF Record header }

View File

@ -78,7 +78,7 @@ type
procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook); procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook);
procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook); procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook);
procedure ReadBoundsheet(AStream: TStream); procedure ReadBoundsheet(AStream: TStream);
function ReadString(const AStream: TStream; const ALength: WORD): UTF8String; function ReadString(const AStream: TStream; const ALength: WORD): String;
protected protected
procedure ReadFont(const AStream: TStream); procedure ReadFont(const AStream: TStream);
procedure ReadFormat(AStream: TStream); override; procedure ReadFormat(AStream: TStream); override;
@ -219,7 +219,7 @@ var
implementation implementation
uses uses
Math, fpsStrings, fpsStreams, fpsExprParser; Math, lconvencoding, fpsStrings, fpsStreams, fpsExprParser;
const const
{ Excel record IDs } { Excel record IDs }
@ -349,7 +349,7 @@ var
lLen: SizeInt; lLen: SizeInt;
RecordType: WORD; RecordType: WORD;
RecordSize: WORD; RecordSize: WORD;
C: char; C: WideChar;
begin begin
StringFlags:=AStream.ReadByte; StringFlags:=AStream.ReadByte;
Dec(PendingRecordSize); Dec(PendingRecordSize);
@ -376,17 +376,17 @@ begin
end; end;
Result := WideStringLEToN(Result); Result := WideStringLEToN(Result);
end else begin end else begin
//String is 1 byte per char, this is UTF-16 with the high byte ommited because it is zero // String is 1 byte per char, this is UTF-16 with the high byte ommited
//so decompress and then convert // because it is zero, so decompress and then convert
lLen := ALength; lLen := ALength;
SetLength(DecomprStrValue, lLen); SetLength(DecomprStrValue, lLen);
for i := 1 to lLen do for i := 1 to lLen do
begin begin
C:=WideChar(AStream.ReadByte()); C := WideChar(AStream.ReadByte); // Read 1 byte, but put it into a 2-byte char
DecomprStrValue[i] := C; DecomprStrValue[i] := C;
Dec(PendingRecordSize); Dec(PendingRecordSize);
if (PendingRecordSize <= 0) and (i < lLen) then begin if (PendingRecordSize <= 0) and (i < lLen) then begin
//A CONTINUE may happend here //A CONTINUE may have happened here
RecordType := WordLEToN(AStream.ReadWord); RecordType := WordLEToN(AStream.ReadWord);
RecordSize := WordLEToN(AStream.ReadWord); RecordSize := WordLEToN(AStream.ReadWord);
if RecordType <> INT_EXCEL_ID_CONTINUE then begin if RecordType <> INT_EXCEL_ID_CONTINUE then begin
@ -398,7 +398,6 @@ begin
end; end;
end; end;
end; end;
Result := DecomprStrValue; Result := DecomprStrValue;
end; end;
if StringFlags and 8 = 8 then begin if StringFlags and 8 = 8 then begin
@ -572,7 +571,7 @@ begin
end; end;
function TsSpreadBIFF8Reader.ReadString(const AStream: TStream; function TsSpreadBIFF8Reader.ReadString(const AStream: TStream;
const ALength: WORD): UTF8String; const ALength: WORD): String;
begin begin
Result := UTF16ToUTF8(ReadWideString(AStream, ALength)); Result := UTF16ToUTF8(ReadWideString(AStream, ALength));
end; end;
@ -618,14 +617,12 @@ begin
BIFF8EOF := False; BIFF8EOF := False;
{ Read workbook globals } { Read workbook globals }
ReadWorkbookGlobals(AStream, AData); ReadWorkbookGlobals(AStream, AData);
// Check for the end of the file // Check for the end of the file
if AStream.Position >= AStream.Size then BIFF8EOF := True; if AStream.Position >= AStream.Size then BIFF8EOF := True;
{ Now read all worksheets } { Now read all worksheets }
while (not BIFF8EOF) do while (not BIFF8EOF) do
begin begin
//Safe to not read beyond assigned worksheet names. //Safe to not read beyond assigned worksheet names.
@ -647,9 +644,7 @@ begin
FWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8)); FWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
{ Finalizations } { Finalizations }
FWorksheetNames.Free; FWorksheetNames.Free;
end; end;
procedure TsSpreadBIFF8Reader.ReadLabel(AStream: TStream); procedure TsSpreadBIFF8Reader.ReadLabel(AStream: TStream);
@ -956,20 +951,22 @@ end;
{ Helper function for reading a string with 8-bit length. } { Helper function for reading a string with 8-bit length. }
function TsSpreadBIFF8Reader.ReadString_8bitLen(AStream: TStream): String; function TsSpreadBIFF8Reader.ReadString_8bitLen(AStream: TStream): String;
const
HAS_8BITLEN = true;
var var
s: widestring; wideStr: widestring;
begin begin
s := ReadWideString(AStream, true); wideStr := ReadWideString(AStream, HAS_8BITLEN);
Result := UTF8Encode(s); Result := UTF8Encode(wideStr);
end; end;
procedure TsSpreadBIFF8Reader.ReadStringRecord(AStream: TStream); procedure TsSpreadBIFF8Reader.ReadStringRecord(AStream: TStream);
var var
s: String; wideStr: WideString;
begin begin
s := ReadWideString(AStream, false); wideStr := ReadWideString(AStream, false);
if (FIncompleteCell <> nil) and (s <> '') then begin if (FIncompleteCell <> nil) and (wideStr <> '') then begin
FIncompleteCell^.UTF8StringValue := UTF8Encode(s); FIncompleteCell^.UTF8StringValue := UTF8Encode(wideStr);
FIncompleteCell^.ContentType := cctUTF8String; FIncompleteCell^.ContentType := cctUTF8String;
if FIsVirtualMode then if FIsVirtualMode then
Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell); Workbook.OnReadCellData(Workbook, FIncompleteCell^.Row, FIncompleteCell^.Col, FIncompleteCell);
@ -984,14 +981,12 @@ procedure TsSpreadBIFF8Reader.ReadXF(const AStream: TStream);
begin begin
case dw of case dw of
$01..$07: result := TsLineStyle(dw-1); $01..$07: result := TsLineStyle(dw-1);
// $07: Result := lsDotted;
else Result := lsDashed; else Result := lsDashed;
end; end;
end; end;
var var
rec: TBIFF8_XFRecord; rec: TBIFF8_XFRecord;
fmt: TsCellFormat; fmt: TsCellFormat;
// xf: TXFRecord;
b: Byte; b: Byte;
dw: DWord; dw: DWord;
fill: Integer; fill: Integer;
@ -1271,9 +1266,8 @@ var
pane: Byte; pane: Byte;
begin begin
{ Write workbook globals } { Write workbook globals }
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
WriteCodePage(AStream, 'ucs2le'); //seUTF16);
WriteWindow1(AStream); WriteWindow1(AStream);
WriteFonts(AStream); WriteFonts(AStream);
WriteNumFormats(AStream); WriteNumFormats(AStream);
@ -1293,7 +1287,6 @@ begin
WriteEOF(AStream); WriteEOF(AStream);
{ Write each worksheet } { Write each worksheet }
for i := 0 to Workbook.GetWorksheetCount - 1 do for i := 0 to Workbook.GetWorksheetCount - 1 do
begin begin
FWorksheet := Workbook.GetWorksheetByIndex(i); FWorksheet := Workbook.GetWorksheetByIndex(i);
@ -1483,7 +1476,7 @@ begin
if AFont.Size <= 0.0 then if AFont.Size <= 0.0 then
raise Exception.Create('Font size not specified.'); raise Exception.Create('Font size not specified.');
WideFontName := AFont.FontName; WideFontName := UTF8Decode(AFont.FontName);
Len := Length(WideFontName); Len := Length(WideFontName);
{ BIFF Record header } { BIFF Record header }

View File

@ -4,20 +4,13 @@ unit xlscommon;
OpenOffice Microsoft Excel File Format document } OpenOffice Microsoft Excel File Format document }
{$ifdef fpc} {$ifdef fpc}
{$mode delphi}{$H+} {$mode objfpc}{$H+}
{$endif} {$endif}
interface interface
uses uses
Classes, SysUtils, DateUtils, Classes, SysUtils, DateUtils,
(*
{$ifdef USE_NEW_OLE}
fpolebasic,
{$else}
fpolestorage,
{$endif}
*)
fpstypes, fpspreadsheet, fpsutils, lconvencoding; fpstypes, fpspreadsheet, fpsutils, lconvencoding;
const const
@ -73,6 +66,11 @@ const
{ CODEPAGE record constants } { CODEPAGE record constants }
WORD_ASCII = 367; 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_UTF_16 = 1200; // BIFF 8
WORD_CP_1250_Latin2 = 1250; WORD_CP_1250_Latin2 = 1250;
WORD_CP_1251_Cyrillic = 1251; WORD_CP_1251_Cyrillic = 1251;
@ -304,6 +302,7 @@ type
FDateMode: TDateMode; FDateMode: TDateMode;
FLastRow: Cardinal; FLastRow: Cardinal;
FLastCol: Cardinal; FLastCol: Cardinal;
FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding
procedure CreateNumFormatList; override; procedure CreateNumFormatList; override;
function FindXFIndex(ACell: PCell): Integer; virtual; function FindXFIndex(ACell: PCell): Integer; virtual;
function FixColor(AColor: TsColor): TsColor; override; function FixColor(AColor: TsColor): TsColor; override;
@ -321,7 +320,7 @@ type
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override; const AValue: Boolean; ACell: PCell); override;
// Writes out used codepage for character encoding // 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) // Writes out column info(s)
procedure WriteColInfo(AStream: TStream; ACol: PCol); procedure WriteColInfo(AStream: TStream; ACol: PCol);
procedure WriteColInfos(AStream: TStream; ASheet: TsWorksheet); procedure WriteColInfos(AStream: TStream; ASheet: TsWorksheet);
@ -403,7 +402,7 @@ implementation
uses uses
AVL_Tree, Math, Variants, AVL_Tree, Math, Variants,
{%H-}fpspatches, xlsConst, fpsNumFormatParser, fpsrpn, fpsExprParser; {%H-}fpspatches, fpsStrings, xlsConst, fpsNumFormatParser, fpsrpn, fpsExprParser;
const const
{ Helper table for rpn formulas: { Helper table for rpn formulas:
@ -828,18 +827,12 @@ begin
case lCodePage of case lCodePage of
// 016FH = 367 = ASCII // 016FH = 367 = ASCII
// 01B5H = 437 = IBM PC CP-437 (US) WORD_CP_437_DOS_US: FCodePage := 'cp437'; // IBM PC CP-437 (US)
//02D0H = 720 = IBM PC CP-720 (OEM Arabic) //02D0H = 720 = IBM PC CP-720 (OEM Arabic)
//02E1H = 737 = IBM PC CP-737 (Greek) //02E1H = 737 = IBM PC CP-737 (Greek)
//0307H = 775 = IBM PC CP-775 (Baltic) //0307H = 775 = IBM PC CP-775 (Baltic)
//0352H = 850 = IBM PC CP-850 (Latin I) WORD_CP_850_DOS_Latin1: FCodepage := 'cp850'; // IBM PC CP-850 (Latin I)
$0352: FCodepage := 'cp850'; WORD_CP_852_DOS_Latin2: FCodepage := 'cp852'; // IBM PC CP-852 (Latin II (Central European))
//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) //035AH = 858 = IBM PC CP-858 (Multilingual Latin I with Euro)
//035CH = 860 = IBM PC CP-860 (Portuguese) //035CH = 860 = IBM PC CP-860 (Portuguese)
//035DH = 861 = IBM PC CP-861 (Icelandic) //035DH = 861 = IBM PC CP-861 (Icelandic)
@ -847,29 +840,27 @@ begin
//035FH = 863 = IBM PC CP-863 (Canadian (French)) //035FH = 863 = IBM PC CP-863 (Canadian (French))
//0360H = 864 = IBM PC CP-864 (Arabic) //0360H = 864 = IBM PC CP-864 (Arabic)
//0361H = 865 = IBM PC CP-865 (Nordic) //0361H = 865 = IBM PC CP-865 (Nordic)
//0362H = 866 = IBM PC CP-866 (Cyrillic (Russian)) WORD_CP_866_DOS_Cyrillic: FCodePage := 'cp866'; // IBM PC CP-866 (Cyrillic Russian)
//0365H = 869 = IBM PC CP-869 (Greek (Modern)) //0365H = 869 = IBM PC CP-869 (Greek (Modern))
//036AH = 874 = Windows CP-874 (Thai) WORD_CP_874_Thai: FCodePage := 'cp874'; // 874 = Windows CP-874 (Thai)
//03A4H = 932 = Windows CP-932 (Japanese Shift-JIS) //03A4H = 932 = Windows CP-932 (Japanese Shift-JIS)
//03A8H = 936 = Windows CP-936 (Chinese Simplified GBK) //03A8H = 936 = Windows CP-936 (Chinese Simplified GBK)
//03B5H = 949 = Windows CP-949 (Korean (Wansung)) //03B5H = 949 = Windows CP-949 (Korean (Wansung))
//03B6H = 950 = Windows CP-950 (Chinese Traditional BIG5) //03B6H = 950 = Windows CP-950 (Chinese Traditional BIG5)
//04B0H = 1200 = UTF-16 (BIFF8) WORD_UTF_16 : FCodePage := 'ucs2le'; // UTF-16 (BIFF8)
$04B0: FCodepage := 'utf-16'; WORD_CP_1250_Latin2: FCodepage := 'cp1250'; // Windows CP-1250 (Latin II) (Central European)
//04E2H = 1250 = Windows CP-1250 (Latin II) (Central European) WORD_CP_1251_Cyrillic: FCodePage := 'cp1251'; // Windows CP-1251 (Cyrillic)
//04E3H = 1251 = Windows CP-1251 (Cyrillic) WORD_CP_1252_Latin1: FCodePage := 'cp1252'; // Windows CP-1252 (Latin I) (BIFF4-BIFF5)
//04E4H = 1252 = Windows CP-1252 (Latin I) (BIFF4-BIFF5) WORD_CP_1253_Greek: FCodePage := 'cp1253'; // Windows CP-1253 (Greek)
//04E5H = 1253 = Windows CP-1253 (Greek) WORD_CP_1254_Turkish: FCodepage := 'cp1254'; // Windows CP-1254 (Turkish)
//04E6H = 1254 = Windows CP-1254 (Turkish) WORD_CP_1255_Hebrew: FCodePage := 'cp1255'; // Windows CP-1255 (Hebrew)
$04E6: FCodepage := 'cp1254'; WORD_CP_1256_Arabic: FCodePage := 'cp1256'; // Windows CP-1256 (Arabic)
//04E7H = 1255 = Windows CP-1255 (Hebrew) WORD_CP_1257_Baltic: FCodePage := 'cp1257'; // Windows CP-1257 (Baltic)
//04E8H = 1256 = Windows CP-1256 (Arabic) WORD_CP_1258_Vietnamese: FCodePage := 'cp1258'; // Windows CP-1258 (Vietnamese)
//04E9H = 1257 = Windows CP-1257 (Baltic)
//04EAH = 1258 = Windows CP-1258 (Vietnamese)
//0551H = 1361 = Windows CP-1361 (Korean (Johab)) //0551H = 1361 = Windows CP-1361 (Korean (Johab))
//2710H = 10000 = Apple Roman //2710H = 10000 = Apple Roman
//8000H = 32768 = Apple Roman //8000H = 32768 = Apple Roman
//8001H = 32769 = Windows CP-1252 (Latin I) (BIFF2-BIFF3) WORD_CP_1258_Latin1_BIFF2_3: FCodePage := 'cp1252'; // Windows CP-1252 (Latin I) (BIFF2-BIFF3)
end; end;
end; end;
@ -1711,7 +1702,8 @@ begin
len := AStream.ReadByte; len := AStream.ReadByte;
SetLength(s, len); SetLength(s, len);
AStream.ReadBuffer(s[1], len); AStream.ReadBuffer(s[1], len);
Result := ansiToUTF8(s); Result := ConvertEncoding(s, FCodePage, encodingUTF8);
// Result := ansiToUTF8(s);
end; end;
{ Reads a STRING record. It immediately precedes a FORMULA record which has a { Reads a STRING record. It immediately precedes a FORMULA record which has a
@ -1806,7 +1798,7 @@ end;
function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer; function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
begin begin
FLastRow := 0; FLastRow := 0;
IterateThroughCells(nil, AWorksheet.Cells, GetLastRowCallback); IterateThroughCells(nil, AWorksheet.Cells, @GetLastRowCallback);
Result := FLastRow; Result := FLastRow;
end; end;
@ -1819,7 +1811,7 @@ end;
function TsSpreadBIFFWriter.GetLastColIndex(AWorksheet: TsWorksheet): Word; function TsSpreadBIFFWriter.GetLastColIndex(AWorksheet: TsWorksheet): Word;
begin begin
FLastCol := 0; FLastCol := 0;
IterateThroughCells(nil, AWorksheet.Cells, GetLastColCallback); IterateThroughCells(nil, AWorksheet.Cells, @GetLastColCallback);
Result := FLastCol; Result := FLastCol;
end; end;
@ -1878,28 +1870,44 @@ begin
AStream.WriteBuffer(rec, SizeOf(rec)); AStream.WriteBuffer(rec, SizeOf(rec));
end; 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 var
lCodepage: Word; cp: Word;
begin begin
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_CODEPAGE)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_CODEPAGE));
AStream.WriteWord(WordToLE(2)); AStream.WriteWord(WordToLE(2));
{ Codepage } { Codepage }
case AEncoding of FCodePage := lowercase(ACodePage);
seLatin2: lCodepage := WORD_CP_1250_Latin2; case FCodePage of
seCyrillic: lCodepage := WORD_CP_1251_Cyrillic; 'ucs2le': cp := WORD_UTF_16; // Biff 7
seGreek: lCodepage := WORD_CP_1253_Greek; 'cp437' : cp := WORD_CP_437_DOS_US;
seTurkish: lCodepage := WORD_CP_1254_Turkish; 'cp850' : cp := WORD_CP_850_DOS_Latin1;
seHebrew: lCodepage := WORD_CP_1255_Hebrew; 'cp852' : cp := WORD_CP_852_DOS_Latin2;
seArabic: lCodepage := WORD_CP_1256_Arabic; '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 else
// Default is Latin1 Workbook.AddErrorMsg(rsCodePageNotSupported, [FCodePage]);
lCodepage := WORD_CP_1252_Latin1; FCodePage := 'cp1252';
cp := WORD_CP_1252_Latin1;
end; end;
AStream.WriteWord(WordToLE(lCodepage)); AStream.WriteWord(WordToLE(cp));
end; end;
{ Writes column info for the given column. Currently only the colum width is used. { Writes column info for the given column. Currently only the colum width is used.
@ -1958,7 +1966,8 @@ begin
end; end;
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); procedure TsSpreadBIFFWriter.WriteComment(AStream: TStream; ACell: PCell);
const const
CHUNK_SIZE = 2048; CHUNK_SIZE = 2048;
@ -1977,7 +1986,7 @@ begin
List := TStringList.Create; List := TStringList.Create;
try try
List.Text := UTF8ToAnsi(ACell^.Comment); List.Text := ConvertEncoding(ACell^.Comment, encodingUTF8, FCodePage);
comment := List[0]; comment := List[0];
for p := 1 to List.Count-1 do for p := 1 to List.Count-1 do
comment := comment + #$0A + List[p]; comment := comment + #$0A + List[p];
@ -2402,8 +2411,8 @@ begin
shared formula RECORD here. The shared formula RECORD must follow the shared formula RECORD here. The shared formula RECORD must follow the
first FORMULA record referring to the shared formula} first FORMULA record referring to the shared formula}
if (ACell^.SharedFormulaBase <> nil) and if (ACell^.SharedFormulaBase <> nil) and
(ARow = ACell^.SharedFormulaBase.Row) and (ARow = ACell^.SharedFormulaBase^.Row) and
(ACol = ACell^.SharedFormulaBase.Col) (ACol = ACell^.SharedFormulaBase^.Col)
then then
WriteSharedFormula(AStream, ACell^.SharedFormulaBase); WriteSharedFormula(AStream, ACell^.SharedFormulaBase);
@ -2484,8 +2493,8 @@ var
begin begin
rec.FormulaSize := WordToLE(5); rec.FormulaSize := WordToLE(5);
rec.Token := INT_EXCEL_TOKEN_TEXP; // Marks the cell for using a shared formula rec.Token := INT_EXCEL_TOKEN_TEXP; // Marks the cell for using a shared formula
rec.Row := WordToLE(ACell^.SharedFormulaBase.Row); rec.Row := WordToLE(ACell^.SharedFormulaBase^.Row);
rec.Col := WordToLE(ACell^.SharedFormulaBase.Col); rec.Col := WordToLE(ACell^.SharedFormulaBase^.Col);
AStream.WriteBuffer(rec, SizeOf(rec)); AStream.WriteBuffer(rec, SizeOf(rec));
RPNLength := SizeOf(rec); RPNLength := SizeOf(rec);
end; end;
@ -2929,7 +2938,7 @@ var
len: Byte; len: Byte;
s: ansistring; s: ansistring;
begin begin
s := UTF8ToAnsi(AString); s := ConvertEncoding(AString, encodingUTF8, FCodePage);
len := Length(s); len := Length(s);
AStream.WriteByte(len); AStream.WriteByte(len);
AStream.WriteBuffer(s[1], len); AStream.WriteBuffer(s[1], len);

View File

@ -2395,6 +2395,8 @@ end;
procedure TsSpreadOOXMLWriter.WriteWorksheetRels(AWorksheet: TsWorksheet); procedure TsSpreadOOXMLWriter.WriteWorksheetRels(AWorksheet: TsWorksheet);
begin begin
Unused(AWorksheet);
// Create stream // Create stream
SetLength(FSSheetRels, FCurSheetNum + 1); SetLength(FSSheetRels, FCurSheetNum + 1);
if (boBufStream in Workbook.Options) then if (boBufStream in Workbook.Options) then