fpspreadsheet: Fix biff8 writer to use CONTINUE records if text in STRING record is too long.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6055 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2017-11-03 18:43:37 +00:00
parent 0310e5587e
commit f20779983f
12 changed files with 147 additions and 44 deletions

View File

@ -8,7 +8,7 @@ msgstr ""
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.9\n"
"X-Generator: Poedit 2.0.4\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: de\n"
@ -310,15 +310,15 @@ msgstr "Palettenindex %d"
#: fpsstrings.rspasswordremoved_biff2
msgid "Password removed (BIFF2 requires matching workbook and worksheet passwords)"
msgstr ""
msgstr "Das Passwort wurde entfernt (BIFF2 erfordert übereinstimmende Passwörter von Workbook und Worksheet "
#: fpsstrings.rspasswordremoved_excel
msgid "Password removed (Hashing algorithm not compatible with Excel)"
msgstr ""
msgstr "Das Passwort wurde entfernt (Der Hashing-Algorithmus ist nicht mit Excel kompatibel)."
#: fpsstrings.rspasswordremoved_notvalid
msgid "Password removed (Not valid)."
msgstr ""
msgstr "Das Passwort wurde entfernt (ungültig)."
#: fpsstrings.rspurple
msgid "purple"
@ -364,6 +364,10 @@ msgstr "transparent"
msgid "Text value exceeds the %d character limit in cell %s and has been truncated."
msgstr "Der Text überschreitet die Grenze von %d Zeichen in Zelle %s und wurde abgeschnitten."
#: fpsstrings.rstruncatetoolongtext
msgid "Text value exceeds the %d character limit and has been truncated."
msgstr "Der Text ist länger als die Grenze bei %d Zeichen und wurde gekürzt."
#: fpsstrings.rsunexpectedendofexpression
msgid "Unexpected end of expression"
msgstr "Unerwartetes Ende des Ausdrucks"
@ -431,4 +435,3 @@ msgstr "Die Datei kann nicht geschrieben werden, weil der Name des Arbeitsblatte
#: fpsstrings.rsyellow
msgid "yellow"
msgstr "gelb"

View File

@ -353,6 +353,10 @@ msgstr "läpinäkyvä"
msgid "Text value exceeds the %d character limit in cell %s and has been truncated."
msgstr ""
#: fpsstrings.rstruncatetoolongtext
msgid "Text value exceeds the %d character limit and has been truncated."
msgstr ""
#: fpsstrings.rsunexpectedendofexpression
msgid "Unexpected end of expression"
msgstr ""

View File

@ -367,6 +367,10 @@ msgstr "átlátszó"
msgid "Text value exceeds the %d character limit in cell %s and has been truncated."
msgstr "A szöveg hosszabb mint a(z) 15 karakteres korlát a(z) %s cellában, ezért csonkolva lett."
#: fpsstrings.rstruncatetoolongtext
msgid "Text value exceeds the %d character limit and has been truncated."
msgstr ""
#: fpsstrings.rsunexpectedendofexpression
msgid "Unexpected end of expression"
msgstr "A kifejezés váratlanul ért véget"

View File

@ -353,6 +353,10 @@ msgstr ""
msgid "Text value exceeds the %d character limit in cell %s and has been truncated."
msgstr ""
#: fpsstrings.rstruncatetoolongtext
msgid "Text value exceeds the %d character limit and has been truncated."
msgstr ""
#: fpsstrings.rsunexpectedendofexpression
msgid "Unexpected end of expression"
msgstr ""

View File

@ -357,6 +357,10 @@ msgstr "прозрачно"
msgid "Text value exceeds the %d character limit in cell %s and has been truncated."
msgstr "Текстовое выражения ограничено %d символами в ячейке %s и будет уменьшено."
#: fpsstrings.rstruncatetoolongtext
msgid "Text value exceeds the %d character limit and has been truncated."
msgstr ""
#: fpsstrings.rsunexpectedendofexpression
msgid "Unexpected end of expression"
msgstr "Ожидается конец выражения"

View File

@ -280,7 +280,7 @@ type
procedure WriteToStream(AStream: TStream; AParams: TsStreamParams = []); override;
end;
{ procedure WriteStarObjectDescriptorToStream(AStream: TStream); }
procedure InitOpenDocLimitations(out ALimitations: TsSpreadsheetFormatLimitations);
var
sfidOpenDocument: TsSpreadFormatID;
@ -452,6 +452,16 @@ begin
end; *)
procedure InitOpenDocLimitations(out ALimitations: TsSpreadsheetFormatLimitations);
begin
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
ALimitations.MaxColCount := 1024;
ALimitations.MaxRowCount := 1048576;
//https://forum.openoffice.org/en/forum/viewtopic.php?f=9&t=11247&p=52985
ALimitations.MaxCharsInTextCell := 65535;
end;
{******************************************************************************}
{ TXMLHeaderFooterFont }
{******************************************************************************}
@ -993,9 +1003,7 @@ constructor TsSpreadOpenDocReader.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
FLimitations.MaxColCount := 1024;
FLimitations.MaxRowCount := 1048576;
InitOpenDocLimitations(FLimitations);
FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.';
@ -6280,9 +6288,7 @@ begin
FPointSeparatorSettings.DecimalSeparator:='.';
FPointSeparatorSettings.ListSeparator := ';'; // for formulas
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
FLimitations.MaxColCount := 1024;
FLimitations.MaxRowCount := 1048576;
InitOpenDocLimitations(FLimitations);
end;
destructor TsSpreadOpenDocWriter.Destroy;

View File

@ -37,6 +37,8 @@ resourcestring
'the best-matching palette colors.';
rsTruncateTooLongCellText = 'Text value exceeds the %d character limit in ' +
'cell %s and has been truncated.';
rsTruncateTooLongText = 'Text value exceeds the %d character limit ' +
'and has been truncated.';
rsWriteError_WorksheetNameTooLong = 'File cannot be written because ' +
'the name of worksheet "%0:s" is too long (max %1:d characters).';

View File

@ -45,6 +45,7 @@ type
MaxColCount: Cardinal;
MaxPaletteSize: Integer;
MaxSheetNameLength: Integer;
MaxCharsInTextCell: Integer;
end;
const

View File

@ -170,6 +170,9 @@ var
sfidExcel2: TsSpreadFormatID;
procedure InitBiff2Limitations(out ALimitations: TsSpreadsheetFormatLimitations);
implementation
uses
@ -270,6 +273,12 @@ type
end;
procedure InitBiff2Limitations(out ALimitations: TsSpreadsheetFormatLimitations);
begin
InitBiffLimitations(ALimitations);
ALimitations.MaxPaletteSize := BIFF2_MAX_PALETTE_SIZE;
end;
procedure InternalAddBuiltinNumFormats(AList: TStringList; AFormatSettings: TFormatSettings);
var
fs: TFormatSettings absolute AFormatSettings;
@ -311,13 +320,12 @@ end;
constructor TsSpreadBIFF2Reader.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
FLimitations.MaxPaletteSize := BIFF2_MAX_PALETTE_SIZE;
InitBiff2Limitations(FLimitations);
end;
procedure TsSpreadBIFF2Reader.AddBuiltInNumFormats;
begin
FFirstNumFormatIndexInFile := 0;
//InternalAddBuiltInNumFormats(FNumFormatList, Workbook.FormatSettings);
end;
procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream);
@ -1203,7 +1211,9 @@ end;
constructor TsSpreadBIFF2Writer.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
FLimitations.MaxPaletteSize := BIFF2_MAX_PALETTE_SIZE;
InitBiff2Limitations(FLimitations);
FDateMode := Excel2Settings.DateMode;
FCodePage := Excel2Settings.CodePage;
FSheetIndex := Excel2Settings.SheetIndex;

View File

@ -403,6 +403,7 @@ const
SHAPEID_BASE = 1024;
MAX_BYTES_IN_RECORD = 8224; // without header
MAX_CHARS_IN_WIDESTRING = 32758;
type
@ -3179,13 +3180,10 @@ end;
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.WriteLABEL(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: String; ACell: PCell);
const
//limit for this format: 32767 characters (2 byte each) - header:
//37267-8-1=32758
MAXCHARS = 32758;
var
L: Word;
WideStr: WideString;
recSize: Integer;
rec: TBIFF8_LabelRecord;
recSST: TBIFF8_LabelSSTRecord;
buf: array of byte;
@ -3195,8 +3193,8 @@ begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
// If string is in SST write a LABELSST record
idx := IndexOfSharedString(ACell^.UTF8StringValue, ACell^.RichTextParams);
if idx > -1 then begin
recSST.RecordID := WordToLE(INT_EXCEL_ID_LABELSST);
recSST.RecordSize := WordToLE(SizeOf(recSST) - SizeOf(TsBiffHeader));
@ -3208,6 +3206,7 @@ begin
exit;
end;
// If string is not in SST write a standard LABEL cell
WideStr := UTF8Decode(FixLineEnding(AValue)); //to UTF16
if WideStr = '' then begin
// Badly formatted UTF8String (maybe ANSI?)
@ -3218,15 +3217,12 @@ begin
Exit;
end;
// wp: THIS IS PROBABLY WRONG, BECAUSE A RECORD CAN ONLY CONTAIN 8224 BYTES AND
// A CONTINUE RECORD MUST BE USED!
if Length(WideStr) > MAXCHARS then begin
if Length(WideStr) > FLimitations.MaxCharsInTextCell then begin
// Rather than lose data when reading it, let the application programmer deal
// with the problem or purposefully ignore it.
SetLength(WideStr, MAXCHARS); //may corrupt the string (e.g. in surrogate pairs), but... too bad.
SetLength(WideStr, FLimitations.MaxCharsInTextCell); //may corrupt the string (e.g. in surrogate pairs), but... too bad.
Workbook.AddErrorMsg(rsTruncateTooLongCellText, [
MAXCHARS, GetCellString(ARow, ACol)
FLimitations.MaxCharsInTextCell, GetCellString(ARow, ACol)
]);
end;
L := Length(WideStr);
@ -3234,6 +3230,12 @@ begin
{ BIFF record header }
rec.RecordID := WordToLE(IfThen(nRuns > 0, INT_EXCEL_ID_RSTRING, INT_EXCEL_ID_LABEL));
(*
recSize := SizeOf(TBiff8_LabelRecord) - SizeOf(TsBiffHeader) + L*SizeOf(WideChar);
if nRuns > 0 then
inc(recSize, SizeOf(Word) + nRunms * SizeOf(TBiff8_RichTextFormattingRun);
if n
*)
rec.RecordSize := SizeOf(TBiff8_LabelRecord) - SizeOf(TsBiffHeader) + L *SizeOf(WideChar);
if nRuns > 0 then
inc(rec.RecordSize, SizeOf(Word) + nRuns * SizeOf(TBiff8_RichTextFormattingRun));
@ -3763,13 +3765,24 @@ begin
s := FixLineEnding(FSharedStringTable.Strings[i]);
isASCII := Is8BitString(s);
if isASCII then
rs := s
else begin
begin
rs := s;
if Length(s) > FLimitations.MaxCharsInTextCell then
begin
SetLength(rs, FLimitations.MaxCharsInTextCell);
FWorkbook.AddErrorMsg(rsTruncateTooLongText, [FLimitations.MaxCharsInTextCell]);
end;
end else
begin
ws := WideStringToLE(UTF8ToUTF16(s));
SetLength(rs, Length(ws) * SizeOf(widechar));
Move(ws[1], rs[1], Length(rs));
if Length(ws) > FLimitations.MaxCharsInTextCell then
begin
SetLength(ws, FLimitations.MaxCharsInTextCell);
FWorkbook.AddErrorMsg(rsTruncateTooLongText, [FLimitations.MaxCharsInTextCell]);
end;
end;
// To do: Truncate if string is too long
rtParams := TsRichTextParams(FSharedStringTable.Objects[i]);
@ -4096,6 +4109,7 @@ end;
{@@ ----------------------------------------------------------------------------
Write the result of a string formula in the preceding record.
In BIFF8 files no STRING record occurs, if the result string is empty.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.WriteSTRINGRecord(AStream: TStream;
AString: String);
@ -4106,20 +4120,51 @@ procedure TsSpreadBIFF8Writer.WriteSTRINGRecord(AStream: TStream;
var
wideStr: widestring;
len: Integer;
strBytes: Integer;
idx: Integer;
needCONTINUE: Boolean;
begin
wideStr := UTF8Decode(AString);
if AString = '' then
exit;
wideStr := WideStringToLE(UTF8Decode(FixLineEnding(AString)));
len := Length(wideStr);
{ BIFF Record header }
strBytes := len * SizeOf(WideChar);
needCONTINUE := 3 + strBytes > MAX_BYTES_IN_RECORD;
if needCONTINUE then
strBytes := MAX_BYTES_IN_RECORD - 4; // -4 = -3 (header) - 1 (even byte count)
{ BIFF STRING record header}
AStream.WriteWord(WordToLE(INT_EXCEL_ID_STRING));
AStream.WriteWord(WordToLE(3 + len*SizeOf(widechar)));
AStream.WriteWord(WordToLE(3 + strBytes));
{ Write widestring length }
AStream.WriteWord(WordToLE(len));
{ Widestring flags, 1=regular unicode LE string }
AStream.WriteByte(1);
{ Write characters }
AStream.WriteBuffer(WideStringToLE(wideStr)[1], len * SizeOf(WideChar));
AStream.WriteBuffer(wideStr[1], strBytes);
idx := 1 + strBytes div SizeOf(WideChar);
while needCONTINUE and (idx < len) do begin
strBytes := (len - idx) * SizeOf(WideChar);
needCONTINUE := strBytes + 1 > MAX_BYTES_IN_RECORD;
if needCONTINUE then
strBytes := MAX_BYTES_IN_RECORD - 2; // -2 = -1 (flag byte) - 1 (for even count)
{ BIFF CONTINUE record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_CONTINUE));
AStream.WriteWord(WordToLE(1 + strBytes)); // for flag byte
{ Widestring flags, 1 = regular unicode LE string }
AStream.WriteByte(1);
{Write characters }
AStream.WriteBuffer(wideStr[idx], strBytes);
inc(idx, strBytes div SizeOf(WideChar));
end;
end;
{@@-----------------------------------------------------------------------------

View File

@ -656,6 +656,8 @@ type
procedure AddBuiltinBiffFormats(AList: TStringList;
AFormatSettings: TFormatSettings; ALastIndex: Integer);
procedure InitBiffLimitations(out ALimitations: TsSpreadsheetFormatLimitations);
implementation
@ -884,6 +886,15 @@ begin
for i:=50 to ALastIndex do AList.Add(''); // not supported/used
end;
procedure InitBiffLimitations(out ALimitations: TsSpreadsheetFormatLimitations);
begin
ALimitations.MaxColCount := 256;
ALimitations.MaxRowCount := 65536;
ALimitations.MaxPaletteSize := 64;
ALimitations.MaxSheetNameLength := 31;
ALimitations.MaxCharsInTextCell := 320000; // 32767 in Excel 2003
end;
{------------------------------------------------------------------------------}
{ TsBIFFDefinedName }
@ -980,9 +991,7 @@ begin
FActivePane := 3;
// Limitations of BIFF5 and BIFF8 file format
FLimitations.MaxColCount := 256;
FLimitations.MaxRowCount := 65536;
FLimitations.MaxPaletteSize := 64;
InitBiffLimitations(FLimitations);
end;
{@@ ----------------------------------------------------------------------------
@ -3076,16 +3085,15 @@ end;
{@@ ----------------------------------------------------------------------------
Constructor of the general BIFF writer.
Initializes the date mode and the limitations of the format.
https://support.microsoft.com/de-de/help/507098
-------------------------------------------------------------------------------}
constructor TsSpreadBIFFWriter.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
// Limitations of BIFF5 and BIFF8 file formats
FLimitations.MaxColCount := 256;
FLimitations.MaxRowCount := 65536;
FLimitations.MaxPaletteSize := 64;
FLimitations.MaxSheetNameLength := 31;
InitBIFFLimitations(FLimitations);
// Initial base date in case it won't be set otherwise.
// Use 1900 to get a bit more range between 1900..1904.

View File

@ -229,6 +229,8 @@ var
sfidOOXML: TsSpreadFormatID;
procedure InitOOXMLLimitations(out ALimitations: TsSpreadsheetFormatLimitations);
implementation
@ -353,6 +355,17 @@ const
);
procedure InitOOXMLLimitations(out ALimitations: TsSpreadsheetFormatLimitations);
begin
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
ALimitations.MaxColCount := 16384;
aLimitations.MaxRowCount := 1048576;
ALimitations.MaxSheetNameLength := 31;
// https://support.office.com/en-us/article/Excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3#ID0EBABAAA=Excel_2007
ALimitations.MaxCharsInTextCell := 32767;
end;
{------------------------------------------------------------------------------}
{ TsSpreadOOXMLReader }
{------------------------------------------------------------------------------}
@ -375,6 +388,8 @@ begin
FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.';
InitOOXMLLimitations(FLimitations);
end;
destructor TsSpreadOOXMLReader.Destroy;
@ -2593,10 +2608,7 @@ begin
FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.';
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
FLimitations.MaxColCount := 16384;
FLimitations.MaxRowCount := 1048576;
FLimitations.MaxSheetNameLength := 31;
InitOOXMLLimitations(FLimitations);
end;