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
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;

View File

@ -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;

View File

@ -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)

View File

@ -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}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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;
{@@ ----------------------------------------------------------------------------

View File

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

View File

@ -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;

View File

@ -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?

View File

@ -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 <b>elements in an expanded formula</b>.
@ -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);

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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)';

View File

@ -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 }

View File

@ -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 }

View File

@ -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 (i<lLen) then begin
//A CONTINUE may happend here
if (PendingRecordSize <= 0) and (i < lLen) then begin
//A CONTINUE may have happened 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;
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 }

View File

@ -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);

View File

@ -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