fpspreadsheet: Convert some more biff methods such records are written in a single block (biff2: WriteLabel, WriteNumber, all: WriteFormat, WriteBlank, WriteColInfo).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3325 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-07-15 21:00:49 +00:00
parent 3374315c8b
commit 4c723b9fc3
7 changed files with 395 additions and 37 deletions

View File

@ -54,5 +54,10 @@
<UseAnsiStrings Value="False"/> <UseAnsiStrings Value="False"/>
</SyntaxOptions> </SyntaxOptions>
</Parsing> </Parsing>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf2Set"/>
</Debugging>
</Linking>
</CompilerOptions> </CompilerOptions>
</CONFIG> </CONFIG>

View File

@ -2995,11 +2995,10 @@ begin
Format('table:number-columns-repeated="%d"', [colsRepeated])); Format('table:number-columns-repeated="%d"', [colsRepeated]));
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<table:table-row table:style-name="%s" %s>', [styleName, rowsRepeatedStr])); '<table:table-row table:style-name="%s" %s>' +
AppendToStream(AStream, Format( '<table:table-cell %s/>' +
'<table:table-cell %s/>', [colsRepeatedStr])); '</table:table-row>',
AppendToStream(AStream, [styleName, rowsRepeatedStr, colsRepeatedStr]));
'</table:table-row>');
r := rr; r := rr;
continue; continue;
@ -3476,8 +3475,8 @@ begin
// The row should already be the correct one // The row should already be the correct one
AppendToStream(AStream, AppendToStream(AStream,
'<table:table-cell office:value-type="string"' + lStyle + '>', '<table:table-cell office:value-type="string"' + lStyle + '>' +
'<text:p>' + UTF8TextToXMLText(AValue) + '</text:p>', '<text:p>' + UTF8TextToXMLText(AValue) + '</text:p>' +
'</table:table-cell>'); '</table:table-cell>');
end; end;
@ -3514,8 +3513,8 @@ begin
end; end;
AppendToStream(AStream, AppendToStream(AStream,
'<table:table-cell office:value-type="' + valType + '" office:value="' + StrValue + '"' + lStyle + '>', '<table:table-cell office:value-type="' + valType + '" office:value="' + StrValue + '"' + lStyle + '>' +
'<text:p>' + DisplayStr + '</text:p>', '<text:p>' + DisplayStr + '</text:p>' +
'</table:table-cell>'); '</table:table-cell>');
end; end;

View File

@ -87,6 +87,8 @@ type
TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter) TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter)
private private
function FindXFIndex(ACell: PCell): Word; function FindXFIndex(ACell: PCell): Word;
procedure GetCellAttributes(ACell: PCell; XFIndex: Word;
out Attrib1, Attrib2, Attrib3: Byte);
{ Record writing methods } { Record writing methods }
procedure WriteBOF(AStream: TStream); procedure WriteBOF(AStream: TStream);
procedure WriteCellFormatting(AStream: TStream; ACell: PCell; XFIndex: Word); procedure WriteCellFormatting(AStream: TStream; ACell: PCell; XFIndex: Word);
@ -850,6 +852,50 @@ begin
end; end;
end; end;
{ Determines the cell attributes needed for writing a cell content record, such
as WriteLabel, WriteNumber, etc.
The cell attributes contain, in bit masks, xf record index, font index, borders, etc.}
procedure TsSpreadBIFF2Writer.GetCellAttributes(ACell: PCell; XFIndex: Word;
out Attrib1, Attrib2, Attrib3: Byte);
begin
if ACell^.UsedFormattingFields = [] then begin
Attrib1 := 15;
Attrib2 := 0;
Attrib3 := 0;
exit;
end;
// 1st byte:
// Mask $3F: Index to XF record
// Mask $40: 1 = Cell is locked
// Mask $80: 1 = Formula is hidden
Attrib1 := Min(XFIndex, $3F) and $3F;
// 2nd byte:
// Mask $3F: Index to FORMAT record
// Mask $C0: Index to FONT record
Attrib2 := ACell^.FontIndex shr 6;
// 3rd byte
// Mask $07: horizontal alignment
// Mask $08: Cell has left border
// Mask $10: Cell has right border
// Mask $20: Cell has top border
// Mask $40: Cell has bottom border
// Mask $80: Cell has shaded background
Attrib3 := 0;
if uffHorAlign in ACell^.UsedFormattingFields then
Attrib3 := ord (ACell^.HorAlignment);
if uffBorder in ACell^.UsedFormattingFields then begin
if cbNorth in ACell^.Border then Attrib3 := Attrib3 or $20;
if cbWest in ACell^.Border then Attrib3 := Attrib3 or $08;
if cbEast in ACell^.Border then Attrib3 := Attrib3 or $10;
if cbSouth in ACell^.Border then Attrib3 := Attrib3 or $40;
end;
if uffBackgroundColor in ACell^.UsedFormattingFields then
Attrib3 := Attrib3 or $80;
end;
{ Builds up the list of number formats to be written to the biff2 file. { Builds up the list of number formats to be written to the biff2 file.
Unlike biff5+ no formats are added here because biff2 supports only 21 Unlike biff5+ no formats are added here because biff2 supports only 21
standard formats; these formats have been added by the NumFormatList's standard formats; these formats have been added by the NumFormatList's
@ -888,7 +934,7 @@ begin
// 2nd byte: // 2nd byte:
// Mask $3F: Index to FORMAT record // Mask $3F: Index to FORMAT record
// Mask $C0: Index to FONT record // Mask $C0: Index to FONT record
w := ACell.FontIndex shl 6; w := ACell.FontIndex shr 6; // was shl --> MUST BE shr!
b := Lo(w); b := Lo(w);
//b := ACell.FontIndex shl 6; //b := ACell.FontIndex shl 6;
AStream.WriteByte(b); AStream.WriteByte(b);
@ -918,10 +964,36 @@ end;
Writes an Excel 2 COLWIDTH record Writes an Excel 2 COLWIDTH record
} }
procedure TsSpreadBIFF2Writer.WriteColWidth(AStream: TStream; ACol: PCol); procedure TsSpreadBIFF2Writer.WriteColWidth(AStream: TStream; ACol: PCol);
type
TColRecord = packed record
RecordID: Word;
RecordSize: Word;
StartCol: Byte;
EndCol: Byte;
ColWidth: Word;
end;
var var
rec: TColRecord;
w: Integer; w: Integer;
begin begin
if Assigned(ACol) then begin if Assigned(ACol) then begin
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_COLWIDTH);
rec.RecordSize := WordToLE(4);
{ Start and end column }
rec.StartCol := ACol^.Col;
rec.EndCol := ACol^.Col;
{ Column width }
{ calculate width to be in units of 1/256 of pixel width of character "0" }
w := round(ACol^.Width * 256);
rec.ColWidth := WordToLE(w);
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLWIDTH)); // BIFF record header AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLWIDTH)); // BIFF record header
AStream.WriteWord(WordToLE(4)); // Record size AStream.WriteWord(WordToLE(4)); // Record size
@ -930,6 +1002,7 @@ begin
{ calculate width to be in units of 1/256 of pixel width of character "0" } { calculate width to be in units of 1/256 of pixel width of character "0" }
w := round(ACol^.Width * 256); w := round(ACol^.Width * 256);
AStream.WriteWord(WordToLE(w)); // write width AStream.WriteWord(WordToLE(w)); // write width
*)
end; end;
end; end;
@ -1257,15 +1330,42 @@ end;
procedure TsSpreadBiff2Writer.WriteFormat(AStream: TStream; procedure TsSpreadBiff2Writer.WriteFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer); AFormatData: TsNumFormatData; AListIndex: Integer);
type
TNumFormatRecord = packed record
RecordID: Word;
RecordSize: Word;
FormatLen: Byte;
end;
var var
len: Integer; len: Integer;
s: ansistring; s: ansistring;
rec: TNumFormatRecord;
buf: array of byte;
begin begin
Unused(AFormatData); Unused(AFormatData);
s := NumFormatList.FormatStringForWriting(AListIndex); s := NumFormatList.FormatStringForWriting(AListIndex);
len := Length(s); len := Length(s);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_FORMAT);
rec.RecordSize := WordToLE(1 + len);
{ Length byte of format string }
rec.FormatLen := len;
{ 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));
{ Write out }
AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*len);
{ Clean up }
SetLength(buf, 0);
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT));
AStream.WriteWord(WordToLE(1 + len)); AStream.WriteWord(WordToLE(1 + len));
@ -1273,6 +1373,7 @@ begin
{ Format string } { Format string }
AStream.WriteByte(len); // AnsiString, char count in 1 byte AStream.WriteByte(len); // AnsiString, char count in 1 byte
AStream.WriteBuffer(s[1], len); // String data AStream.WriteBuffer(s[1], len); // String data
*)
end; end;
procedure TsSpreadBIFF2Writer.WriteFormatCount(AStream: TStream); procedure TsSpreadBIFF2Writer.WriteFormatCount(AStream: TStream);
@ -1401,13 +1502,38 @@ end;
*******************************************************************} *******************************************************************}
procedure TsSpreadBIFF2Writer.WriteBlank(AStream: TStream; procedure TsSpreadBIFF2Writer.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell); const ARow, ACol: Cardinal; ACell: PCell);
type
TBlankRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
Attrib1, Attrib2, Attrib3: Byte;
end;
var var
xf: Word; xf: Word;
rec: TBlankRecord;
begin begin
xf := FindXFIndex(ACell); xf := FindXFIndex(ACell);
if xf >= 63 then if xf >= 63 then
WriteIXFE(AStream, xf); WriteIXFE(AStream, xf);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_BLANK);
rec.RecordSize := WordToLE(7);
{ BIFF record data }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ BIFF2 attributes }
GetCellAttributes(ACell, xf, rec.Attrib1, rec.Attrib2, rec.Attrib3);
{ Write out }
AStream.WriteBuffer(rec, Sizeof(rec));
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK));
AStream.WriteWord(WordToLE(7)); AStream.WriteWord(WordToLE(7));
@ -1418,6 +1544,7 @@ begin
{ BIFF2 Attributes } { BIFF2 Attributes }
WriteCellFormatting(AStream, ACell, xf); WriteCellFormatting(AStream, ACell, xf);
*)
end; end;
{******************************************************************* {*******************************************************************
@ -1433,12 +1560,25 @@ end;
*******************************************************************} *******************************************************************}
procedure TsSpreadBIFF2Writer.WriteLabel(AStream: TStream; const ARow, procedure TsSpreadBIFF2Writer.WriteLabel(AStream: TStream; const ARow,
ACol: Cardinal; const AValue: string; ACell: PCell); ACol: Cardinal; const AValue: string; ACell: PCell);
type
TLabelRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
Attrib1: Byte;
Attrib2: Byte;
Attrib3: Byte;
TextLen: Byte;
end;
const const
MaxBytes=255; //limit for this format MAXBYTES = 255; //limit for this format
var var
L: Byte; L: Byte;
AnsiText: ansistring; AnsiText: ansistring;
TextTooLong: boolean=false; TextTooLong: boolean=false;
rec: TLabelRecord;
buf: array of byte;
var var
xf: Word; xf: Word;
begin begin
@ -1446,14 +1586,13 @@ begin
AnsiText := UTF8ToISO_8859_1(AValue); AnsiText := UTF8ToISO_8859_1(AValue);
if Length(AnsiText)>MaxBytes then if Length(AnsiText) > MAXBYTES then begin
begin
// BIFF 5 does not support labels/text bigger than 255 chars, // BIFF 5 does not support labels/text bigger than 255 chars,
// so BIFF2 won't either // so BIFF2 won't either
// Rather than lose data when reading it, let the application programmer deal // Rather than lose data when reading it, let the application programmer deal
// with the problem or purposefully ignore it. // with the problem or purposefully ignore it.
TextTooLong:=true; TextTooLong:=true;
AnsiText := Copy(AnsiText,1,MaxBytes); AnsiText := Copy(AnsiText, 1, MAXBYTES);
end; end;
L := Length(AnsiText); L := Length(AnsiText);
@ -1461,6 +1600,30 @@ begin
if xf >= 63 then if xf >= 63 then
WriteIXFE(AStream, xf); WriteIXFE(AStream, xf);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_LABEL);
rec.RecordSize := WordToLE(8 + L);
{ BIFF record data }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ BIFF2 attributes }
GetCellAttributes(ACell, xf, rec.Attrib1, rec.Attrib2, rec.Attrib3);
{ Text length: 8 bit }
rec.TextLen := L;
{ Copy the text characters into a buffer immediately after rec }
SetLength(buf, SizeOf(rec) + SizeOf(ansiChar)*L);
Move(rec, buf[0], SizeOf(rec));
Move(AnsiText[1], buf[SizeOf(rec)], L*SizeOf(ansiChar));
{ Write out }
AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*L);
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_LABEL)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_LABEL));
AStream.WriteWord(WordToLE(8 + L)); AStream.WriteWord(WordToLE(8 + L));
@ -1475,6 +1638,7 @@ begin
{ String with 8-bit size } { String with 8-bit size }
AStream.WriteByte(L); AStream.WriteByte(L);
AStream.WriteBuffer(AnsiText[1], L); AStream.WriteBuffer(AnsiText[1], L);
*)
{ {
//todo: keep a log of errors and show with an exception after writing file or something. //todo: keep a log of errors and show with an exception after writing file or something.
@ -1495,13 +1659,25 @@ end;
*******************************************************************} *******************************************************************}
procedure TsSpreadBIFF2Writer.WriteNumber(AStream: TStream; const ARow, procedure TsSpreadBIFF2Writer.WriteNumber(AStream: TStream; const ARow,
ACol: Cardinal; const AValue: double; ACell: PCell); ACol: Cardinal; const AValue: double; ACell: PCell);
type
TNumberRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
Attrib1: Byte;
Attrib2: Byte;
Attrib3: Byte;
Value: Double;
end;
var var
xf: Word; xf: Word;
rec: TNumberRecord;
begin begin
xf := FindXFIndex(ACell); xf := FindXFIndex(ACell);
if xf >= 63 then if xf >= 63 then
WriteIXFE(AStream, xf); WriteIXFE(AStream, xf);
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_NUMBER)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_NUMBER));
AStream.WriteWord(WordToLE(15)); AStream.WriteWord(WordToLE(15));
@ -1515,6 +1691,24 @@ begin
{ IEE 754 floating-point value } { IEE 754 floating-point value }
AStream.WriteBuffer(AValue, 8); AStream.WriteBuffer(AValue, 8);
*)
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_NUMBER);
rec.RecordSize := WordToLE(15);
{ BIFF record data }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ BIFF2 attributes }
GetCellAttributes(ACell, xf, rec.Attrib1, rec.Attrib2, rec.Attrib3);
{ Number value }
rec.Value := AValue;
{ Write out }
AStream.WriteBuffer(rec, SizeOf(Rec));
end; end;
procedure TsSpreadBIFF2Writer.WriteRow(AStream: TStream; ASheet: TsWorksheet; procedure TsSpreadBIFF2Writer.WriteRow(AStream: TStream; ASheet: TsWorksheet;

View File

@ -667,9 +667,18 @@ end;
*******************************************************************} *******************************************************************}
procedure TsSpreadBiff5Writer.WriteFormat(AStream: TStream; procedure TsSpreadBiff5Writer.WriteFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer); AFormatData: TsNumFormatData; AListIndex: Integer);
type
TNumFormatRecord = packed record
RecordID: Word;
RecordSize: Word;
FormatIndex: Word;
FormatStringLen: Byte;
end;
var var
len: Integer; len: Integer;
s: ansistring; s: ansistring;
rec: TNumFormatRecord;
buf: array of byte;
begin begin
if (AFormatData = nil) or (AFormatData.FormatString = '') then if (AFormatData = nil) or (AFormatData.FormatString = '') then
exit; exit;
@ -677,6 +686,28 @@ begin
s := NumFormatList.FormatStringForWriting(AListIndex); s := NumFormatList.FormatStringForWriting(AListIndex);
len := Length(s); len := Length(s);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_FORMAT);
rec.RecordSize := WordToLE(2 + 1 + len * SizeOf(AnsiChar));
{ Format index }
rec.FormatIndex := WordToLE(AFormatData.Index);
{ Format string }
{ Length in 1 byte }
rec.FormatStringLen := len;
{ 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));
{ Write out }
AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*len);
{ Clean up }
SetLength(buf, 0);
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT));
AStream.WriteWord(WordToLE(2 + 1 + len * SizeOf(AnsiChar))); AStream.WriteWord(WordToLE(2 + 1 + len * SizeOf(AnsiChar)));
@ -687,7 +718,9 @@ begin
{ Format string } { Format string }
AStream.WriteByte(len); // AnsiString, char count in 1 byte AStream.WriteByte(len); // AnsiString, char count in 1 byte
AStream.WriteBuffer(s[1], len * SizeOf(AnsiChar)); // String data AStream.WriteBuffer(s[1], len * SizeOf(AnsiChar)); // String data
*)
end; end;
(* (*
{******************************************************************* {*******************************************************************
* TsSpreadBIFF5Writer.WriteRPNFormula () * TsSpreadBIFF5Writer.WriteRPNFormula ()
@ -893,44 +926,79 @@ end;
*******************************************************************} *******************************************************************}
procedure TsSpreadBIFF5Writer.WriteLabel(AStream: TStream; const ARow, procedure TsSpreadBIFF5Writer.WriteLabel(AStream: TStream; const ARow,
ACol: Cardinal; const AValue: string; ACell: PCell); ACol: Cardinal; const AValue: string; ACell: PCell);
type
TLabelRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
XFIndex: Word;
TextLen: Word;
end;
const const
MaxBytes=255; //limit for this format MAXBYTES = 255; //limit for this format
var var
L: Word; L: Word;
AnsiValue: ansistring; AnsiValue: ansistring;
TextTooLong: boolean=false; TextTooLong: boolean=false;
rec: TLabelRecord;
buf: array of byte;
begin begin
case WorkBookEncoding of case WorkBookEncoding of
seLatin2: AnsiValue := UTF8ToCP1250(AValue); seLatin2: AnsiValue := UTF8ToCP1250(AValue);
seCyrillic: AnsiValue := UTF8ToCP1251(AValue); seCyrillic: AnsiValue := UTF8ToCP1251(AValue);
seGreek: AnsiValue := UTF8ToCP1253(AValue); seGreek: AnsiValue := UTF8ToCP1253(AValue);
seTurkish: AnsiValue := UTF8ToCP1254(AValue); seTurkish: AnsiValue := UTF8ToCP1254(AValue);
seHebrew: AnsiValue := UTF8ToCP1255(AValue); seHebrew: AnsiValue := UTF8ToCP1255(AValue);
seArabic: AnsiValue := UTF8ToCP1256(AValue); seArabic: AnsiValue := UTF8ToCP1256(AValue);
else else
// Latin 1 is the default // Latin 1 is the default
AnsiValue := UTF8ToCP1252(AValue); AnsiValue := UTF8ToCP1252(AValue);
end; end;
if AnsiValue = '' then if AnsiValue = '' then begin
begin
// Bad formatted UTF8String (maybe ANSI?) // Bad formatted UTF8String (maybe ANSI?)
if Length(AValue)<>0 then begin if Length(AValue) <> 0 then begin
//It was an ANSI string written as UTF8 quite sure, so raise exception. //It was an ANSI string written as UTF8 quite sure, so raise exception.
Raise Exception.CreateFmt('Expected UTF8 text but probably ANSI text found in cell [%d,%d]',[ARow,ACol]); Raise Exception.CreateFmt('Expected UTF8 text but probably ANSI text found in cell [%d,%d]',[ARow,ACol]);
end; end;
Exit; Exit;
end; end;
if Length(AnsiValue)>MaxBytes then if Length(AnsiValue) > MAXBYTES then begin
begin
// Rather than lose data when reading it, let the application programmer deal // Rather than lose data when reading it, let the application programmer deal
// with the problem or purposefully ignore it. // with the problem or purposefully ignore it.
TextTooLong := true; TextTooLong := true;
AnsiValue := Copy(AnsiValue,1,MaxBytes); AnsiValue := Copy(AnsiValue, 1, MAXBYTES);
end; end;
L := Length(AnsiValue); L := Length(AnsiValue);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_LABEL);
rec.RecordSize := WordToLE(8 + L);
{ BIFF record data }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ Index to XF record }
rec.XFIndex := WordToLE(FindXFIndex(ACell));
{ String length, 16 bit }
rec.TextLen := WordToLE(L);
{ Copy the text characters into a buffer immediately after rec }
SetLength(buf, SizeOf(rec) + SizeOf(ansiChar)*L);
Move(rec, buf[0], SizeOf(rec));
Move(AnsiValue[1], buf[SizeOf(rec)], L*SizeOf(ansiChar));
{ Write out }
AStream.WriteBuffer(buf[0], SizeOf(Rec) + SizeOf(ansiChar)*L);
{ Clean up }
SetLength(buf, 0);
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_LABEL)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_LABEL));
AStream.WriteWord(WordToLE(8 + L)); AStream.WriteWord(WordToLE(8 + L));
@ -945,7 +1013,7 @@ begin
{ Byte String with 16-bit size } { Byte String with 16-bit size }
AStream.WriteWord(WordToLE(L)); AStream.WriteWord(WordToLE(L));
AStream.WriteBuffer(AnsiValue[1], L); AStream.WriteBuffer(AnsiValue[1], L);
*)
{ {
//todo: keep a log of errors and show with an exception after writing file or something. //todo: keep a log of errors and show with an exception after writing file or something.
We can't just do the following We can't just do the following

View File

@ -687,9 +687,19 @@ end;
procedure TsSpreadBiff8Writer.WriteFormat(AStream: TStream; procedure TsSpreadBiff8Writer.WriteFormat(AStream: TStream;
AFormatData: TsNumFormatData; AListIndex: Integer); AFormatData: TsNumFormatData; AListIndex: Integer);
type
TNumFormatRecord = packed record
RecordID: Word;
RecordSize: Word;
FormatIndex: Word;
FormatStringLen: Word;
FormatStringFlags: Byte;
end;
var var
len: Integer; len: Integer;
s: widestring; s: widestring;
rec: TNumFormatRecord;
buf: array of byte;
begin begin
if (AFormatData = nil) or (AFormatData.FormatString = '') then if (AFormatData = nil) or (AFormatData.FormatString = '') then
exit; exit;
@ -697,6 +707,30 @@ begin
s := NumFormatList.FormatStringForWriting(AListIndex); s := NumFormatList.FormatStringForWriting(AListIndex);
len := Length(s); len := Length(s);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_FORMAT);
rec.RecordSize := WordToLE(2 + 2 + 1 + len * SizeOf(WideChar));
{ Format index }
rec.FormatIndex := WordToLE(AFormatData.Index);
{ Format string }
{ - length of string = 16 bits }
rec.FormatStringLen := WordToLE(len);
{ - Widestring flags, 1 = regular unicode LE string }
rec.FormatStringFlags := 1;
{ - Copy the text characters into a buffer immediately after rec }
SetLength(buf, SizeOf(rec) + SizeOf(WideChar)*len);
Move(rec, buf[0], SizeOf(rec));
Move(s[1], buf[SizeOf(rec)], len*SizeOf(WideChar));
{ Write out }
AStream.WriteBuffer(buf[0], SizeOf(rec) + SizeOf(WideChar)*len);
{ Clean up }
SetLength(buf, 0);
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT));
AStream.WriteWord(WordToLE(2 + 2 + 1 + len * SizeOf(WideChar))); AStream.WriteWord(WordToLE(2 + 2 + 1 + len * SizeOf(WideChar)));
@ -711,6 +745,7 @@ begin
AStream.WriteByte(1); AStream.WriteByte(1);
{ - String data } { - String data }
AStream.WriteBuffer(WideStringToLE(s)[1], len * Sizeof(WideChar)); AStream.WriteBuffer(WideStringToLE(s)[1], len * Sizeof(WideChar));
*)
end; end;
{******************************************************************* {*******************************************************************

View File

@ -1730,7 +1730,31 @@ end;
different record structure. } different record structure. }
procedure TsSpreadBIFFWriter.WriteBlank(AStream: TStream; procedure TsSpreadBIFFWriter.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell); const ARow, ACol: Cardinal; ACell: PCell);
type
TBlankRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
XFIndex: Word;
end;
var
rec: TBlankRecord;
begin begin
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_BLANK);
rec.RecordSize := WordToLE(6);
{ Row and column index }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ Index to XF record, according to formatting }
rec.XFIndex := WordToLE(FindXFIndex(ACell));
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_BLANK));
AStream.WriteWord(WordToLE(6)); AStream.WriteWord(WordToLE(6));
@ -1741,6 +1765,7 @@ begin
{ Index to XF record, according to formatting } { Index to XF record, according to formatting }
WriteXFIndex(AStream, ACell); WriteXFIndex(AStream, ACell);
*)
end; end;
procedure TsSpreadBIFFWriter.WriteCodepage(AStream: TStream; procedure TsSpreadBIFFWriter.WriteCodepage(AStream: TStream;
@ -1770,10 +1795,42 @@ 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.
Valid for BIFF5 and BIFF8 (BIFF2 uses a different record. } Valid for BIFF5 and BIFF8 (BIFF2 uses a different record. }
procedure TsSpreadBIFFWriter.WriteColInfo(AStream: TStream; ACol: PCol); procedure TsSpreadBIFFWriter.WriteColInfo(AStream: TStream; ACol: PCol);
type
TColRecord = packed record
RecordID: Word;
RecordSize: Word;
StartCol: Word;
EndCol: Word;
ColWidth: Word;
XFIndex: Word;
OptionFlags: Word;
NotUsed: Word;
end;
var var
rec: TColRecord;
w: Integer; w: Integer;
begin begin
if Assigned(ACol) then begin if Assigned(ACol) then begin
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_COLINFO);
rec.RecordSize := WordToLE(12);
{ Start and end column }
rec.StartCol := WordToLE(ACol^.Col);
rec.EndCol := WordToLE(ACol^.Col);
{ calculate width to be in units of 1/256 of pixel width of character "0" }
w := round(ACol^.Width * 256);
rec.ColWidth := WordToLE(w);
rec.XFIndex := WordToLE(15); // Index of XF record, not used
rec.OptionFlags := 0; // not used
rec.NotUsed := 0;
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
(*
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLINFO)); // BIFF record header AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLINFO)); // BIFF record header
AStream.WriteWord(WordToLE(12)); // Record size AStream.WriteWord(WordToLE(12)); // Record size
@ -1785,6 +1842,7 @@ begin
AStream.WriteWord(15); // XF record, ignored AStream.WriteWord(15); // XF record, ignored
AStream.WriteWord(0); // option flags, ignored AStream.WriteWord(0); // option flags, ignored
AStream.WriteWord(0); // "not used" AStream.WriteWord(0); // "not used"
*)
end; end;
end; end;

View File

@ -1189,7 +1189,7 @@ end;
procedure TsSpreadOOXMLWriter.WriteLabel(AStream: TStream; const ARow, procedure TsSpreadOOXMLWriter.WriteLabel(AStream: TStream; const ARow,
ACol: Cardinal; const AValue: string; ACell: PCell); ACol: Cardinal; const AValue: string; ACell: PCell);
const const
MaxBytes=32767; //limit for this format MAXBYTES = 32767; //limit for this format
var var
CellPosText: string; CellPosText: string;
lStyleIndex: Cardinal; lStyleIndex: Cardinal;
@ -1201,18 +1201,17 @@ begin
Unused(ARow, ACol, ACell); Unused(ARow, ACol, ACell);
// Office 2007-2010 (at least) support no more characters in a cell; // Office 2007-2010 (at least) support no more characters in a cell;
if Length(AValue)>MaxBytes then if Length(AValue) > MAXBYTES then begin
begin TextTooLong := true;
TextTooLong:=true; ResultingValue := Copy(AValue, 1, MAXBYTES); //may chop off multicodepoint UTF8 characters but well...
ResultingValue:=Copy(AValue,1,MaxBytes); //may chop off multicodepoint UTF8 characters but well...
end end
else else
ResultingValue:=AValue; ResultingValue:=AValue;
AppendToStream(FSSharedStrings, AppendToStream(FSSharedStrings,
'<si>', Format( '<si>' +
'<t>%s</t>', [UTF8TextToXMLText(ResultingValue)]), '<t>' + UTF8TextToXMLText(ResultingValue) + '</t>' +
'</si>' ); '</si>');
CellPosText := TsWorksheet.CellPosToText(ARow, ACol); CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell); lStyleIndex := GetStyleIndex(ACell);