diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index b7055fe75..951e10e3d 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -2939,6 +2939,8 @@ var begin // some abbreviations... defFontSize := Workbook.GetFont(0).Size; + GetSheetDimensions(ASheet, firstRow, lastRow, firstCol, lastCol); + { firstCol := ASheet.GetFirstColIndex; firstRow := ASheet.GetFirstRowIndex; lastCol := ASheet.GetLastColIndex; @@ -2946,6 +2948,7 @@ begin // avoid arithmetic overflow in case of empty worksheet if (firstCol = $FFFFFFFF) and (lastCol = 0) then firstCol := 0; if (FirstRow = $FFFFFFFF) and (lastRow = 0) then firstRow := 0; + } emptyRowsAbove := firstRow > 0; // Now loop through all rows @@ -3103,8 +3106,8 @@ begin FPointSeparatorSettings.DecimalSeparator:='.'; // http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications - FLimitations.MaxCols := 1024; - FLimitations.MaxRows := 1048576; + FLimitations.MaxColCount := 1024; + FLimitations.MaxRowCount := 1048576; end; destructor TsSpreadOpenDocWriter.Destroy; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 17c7af044..327f3af4e 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -26,8 +26,8 @@ type {@@ Record collection limitations of a particular file format } TsSpreadsheetFormatLimitations = record - MaxRows: Cardinal; - MaxCols: Cardinal; + MaxRowCount: Cardinal; + MaxColCount: Cardinal; end; const @@ -6173,8 +6173,8 @@ constructor TsCustomSpreadWriter.Create(AWorkbook: TsWorkbook); begin inherited Create(AWorkbook); { A good starting point valid for many formats... } - FLimitations.MaxCols := 256; - FLimitations.MaxRows := 65536; + FLimitations.MaxColCount := 256; + FLimitations.MaxRowCount := 65536; end; {@@ @@ -6265,7 +6265,8 @@ end; {@@ Determines the size of the worksheet to be written. VirtualMode is respected. - Is called when the writer needs the size for output. + Is called when the writer needs the size for output. Column and row count + limitations are repsected as well. @param AWorksheet Worksheet to be written @param AFirsRow Index of first row to be written @@ -6276,15 +6277,26 @@ end; procedure TsCustomSpreadWriter.GetSheetDimensions(AWorksheet: TsWorksheet; out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); begin - AFirstRow := 0; - AFirstCol := 0; if (boVirtualMode in AWorksheet.Workbook.Options) then begin + AFirstRow := 0; + AFirstCol := 0; ALastRow := AWorksheet.Workbook.VirtualRowCount-1; ALastCol := AWorksheet.Workbook.VirtualColCount-1; end else begin + Workbook.UpdateCaches; + AFirstRow := AWorksheet.GetFirstRowIndex; + AFirstCol := AWorksheet.GetFirstColIndex; ALastRow := AWorksheet.GetLastRowIndex; ALastCol := AWorksheet.GetLastColIndex; end; + if AFirstCol >= Limitations.MaxColCount then + AFirstCol := Limitations.MaxColCount-1; + if AFirstRow >= Limitations.MaxRowCount then + AFirstRow := Limitations.MaxRowCount-1; + if ALastCol >= Limitations.MaxColCount then + ALastCol := Limitations.MaxColCount-1; + if ALastRow >= Limitations.MaxRowCount then + ALastRow := Limitations.MaxRowCount-1; end; {@@ @@ -6307,10 +6319,10 @@ var lastCol, lastRow: Cardinal; begin Workbook.GetLastRowColIndex(lastRow, lastCol); - if lastRow >= FLimitations.MaxRows then - Workbook.AddErrorMsg(lpMaxRowsExceeded, [lastRow+1, FLimitations.MaxRows]); - if lastCol >= FLimitations.MaxCols then - Workbook.AddErrorMsg(lpMaxColsExceeded, [lastCol+1, FLimitations.MaxCols]); + if lastRow >= FLimitations.MaxRowCount then + Workbook.AddErrorMsg(lpMaxRowsExceeded, [lastRow+1, FLimitations.MaxRowCount]); + if lastCol >= FLimitations.MaxColCount then + Workbook.AddErrorMsg(lpMaxColsExceeded, [lastCol+1, FLimitations.MaxColCount]); end; diff --git a/components/fpspreadsheet/tests/errortests.pas b/components/fpspreadsheet/tests/errortests.pas index 2e5840251..4a441610d 100644 --- a/components/fpspreadsheet/tests/errortests.pas +++ b/components/fpspreadsheet/tests/errortests.pas @@ -30,7 +30,6 @@ type procedure TestWriteErrorMessages_BIFF8; procedure TestWriteErrorMessages_ODS; procedure TestWriteErrorMessages_OOXML; - end; implementation @@ -62,10 +61,14 @@ var row, col: Cardinal; row1, row2: Cardinal; col1, col2: Cardinal; + formula: TsFormula; s: String; TempFile: String; ErrList: TStringList; begin + formula.FormulaStr := '=A1'; + formula.DoubleValue := 0.0; + ErrList := TStringList.Create; try // Test 1: Too many rows @@ -78,7 +81,8 @@ begin MyWorksheet.WriteBlank(row, 0); MyWorksheet.WriteNumber(row, 1, 1.0); MyWorksheet.WriteUTF8Text(row, 2, 'A'); - MyWorksheet.WriteRPNFormula(row, 3, CreateRPNFormula( + MyWorksheet.WriteFormula(Row, 3, formula); + MyWorksheet.WriteRPNFormula(row, 4, CreateRPNFormula( RPNCellValue('A1', nil))); end; TempFile:=NewTempFile; @@ -97,10 +101,11 @@ begin col1 := MAX_COL_COUNT[TTestFormat(AFormat)] - 5; col2 := MAX_COL_COUNT[TTestFormat(AFormat)] + 5; for col := col1 to col2 do begin - MyWorksheet.WriteBlank(row, 0); - MyWorksheet.WriteNumber(row, 1, 1.0); - MyWorksheet.WriteUTF8Text(row, 2, 'A'); - MyWorksheet.WriteRPNFormula(row, 3, CreateRPNFormula( + MyWorksheet.WriteBlank(0, col); + MyWorksheet.WriteNumber(1, col, 1.0); + MyWorksheet.WriteUTF8Text(2, col, 'A'); + MyWorksheet.WriteFormula(3, col, formula); + MyWorksheet.WriteRPNFormula(4, col, CreateRPNFormula( RPNCellValue('A1', nil))); end; TempFile:=NewTempFile; diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index 85297ef2b..e4dc9f4dd 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -48,7 +48,6 @@ - @@ -63,7 +62,6 @@ - @@ -78,7 +76,6 @@ - @@ -91,7 +88,6 @@ - @@ -100,17 +96,14 @@ - - - diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index e857e1afb..aa51f57b8 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -1502,7 +1502,7 @@ var s: ansistring; xf: Word; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; RPNLength := 0; @@ -1608,7 +1608,7 @@ var xf: Word; rec: TBlankRecord; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; xf := FindXFIndex(ACell); @@ -1667,7 +1667,7 @@ var var xf: Word; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; if AValue = '' then Exit; // Writing an empty text doesn't work @@ -1730,7 +1730,7 @@ var xf: Word; rec: TBIFF2NumberRecord; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; xf := FindXFIndex(ACell); @@ -1763,8 +1763,8 @@ var w: Word; h: Single; begin - if (ARowIndex >= FLimitations.MaxRows) or (AFirstColIndex >= FLimitations.MaxCols) - or (ALastColIndex >= FLimitations.MaxCols) + if (ARowIndex >= FLimitations.MaxRowCount) or (AFirstColIndex >= FLimitations.MaxColCount) + or (ALastColIndex >= FLimitations.MaxColCount) then exit; diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index 03ab855f2..9ff85c300 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -307,6 +307,16 @@ const ); type + TBIFF5DimensionsRecord = packed record + RecordID: Word; + RecordSize: Word; + FirstRow: Word; + LastRowPlus1: Word; + FirstCol: Word; + LastColPlus1: Word; + NotUsed: Word; + end; + TBIFF5LabelRecord = packed record RecordID: Word; RecordSize: Word; @@ -431,7 +441,7 @@ begin WriteWindow2(AStream, sheet); WritePane(AStream, sheet, true, pane); // true for "is BIFF5 or BIFF8" WriteSelection(AStream, sheet, pane); - WriteRows(AStream, sheet); + //WriteRows(AStream, sheet); if (boVirtualMode in Workbook.Options) then WriteVirtualCells(AStream) @@ -551,8 +561,27 @@ end; } procedure TsSpreadBIFF5Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); var - lLastCol, lLastRow: Word; + rec: TBIFF5DimensionsRecord; + firstCol, lastCol, firstRow, lastRow: Cardinal; + lLastRow, lLastCol: Word; begin + { Determine sheet size } + GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol); + + { Setup BIFF record } + rec.RecordID := WordToLE(INT_EXCEL_ID_DIMENSIONS); + rec.RecordSize := WordToLE(10); + rec.FirstRow := WordToLE(firstRow); + rec.LastRowPlus1 := WordToLE(lastRow+1); + rec.FirstCol := WordToLe(firstCol); + rec.LastColPlus1 := WordToLE(lastCol+1); + rec.NotUsed := 0; + + { Write BIFF record } + AStream.WriteBuffer(rec, SizeOf(rec)); + + (* + { BIFF Record header } AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS)); AStream.WriteWord(WordToLE(10)); @@ -573,6 +602,7 @@ begin { Not used } AStream.WriteWord(0); + *) end; {******************************************************************* @@ -951,7 +981,7 @@ var rec: TBIFF5LabelRecord; buf: array of byte; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; case WorkBookEncoding of diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 07484e6a7..2a31c3f03 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -34,9 +34,11 @@ EOF The row and column numbering in BIFF files is zero-based. Excel file format specification obtained from: - http://sc.openoffice.org/excelfileformat.pdf +see also: +http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP005199291.aspx + AUTHORS: Felipe Monteiro de Carvalho Jose Mejuto } @@ -280,6 +282,16 @@ const ); type + TBIFF8DimensionsRecord = packed record + RecordID: Word; + RecordSize: Word; + FirstRow: DWord; + LastRowPlus1: DWord; + FirstCol: Word; + LastColPlus1: Word; + NotUsed: Word; + end; + TBIFF8LabelRecord = packed record RecordID: Word; RecordSize: Word; @@ -578,7 +590,24 @@ end; procedure TsSpreadBIFF8Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); var firstRow, lastRow, firstCol, lastCol: Cardinal; + rec: TBIFF8DimensionsRecord; begin + { Determine sheet size } + GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol); + + { Populate BIFF record } + rec.RecordID := WordToLE(INT_EXCEL_ID_DIMENSIONS); + rec.RecordSize := WordToLE(14); + rec.FirstRow := DWordToLE(firstRow); + rec.LastRowPlus1 := DWordToLE(lastRow+1); + rec.FirstCol := WordToLE(firstCol); + rec.LastColPlus1 := WordToLE(lastCol+1); + rec.NotUsed := 0; + + { Write BIFF record to stream } + AStream.WriteBuffer(rec, SizeOf(rec)); + + (* { BIFF Record header } AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS)); AStream.WriteWord(WordToLE(14)); @@ -600,6 +629,7 @@ begin { Not used } AStream.WriteWord(WordToLE(0)); + *) end; {******************************************************************* @@ -996,7 +1026,7 @@ var rec: TBIFF8LabelRecord; buf: array of byte; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; WideValue := UTF8Decode(AValue); //to UTF16 diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 527854484..bdff0d1db 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -1852,7 +1852,7 @@ procedure TsSpreadBIFFWriter.WriteBlank(AStream: TStream; var rec: TBIFF58BlankRecord; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; { BIFF record header } @@ -1925,7 +1925,7 @@ var w: Integer; begin if Assigned(ACol) then begin - if (ACol^.Col >= FLimitations.MaxCols) then + if (ACol^.Col >= FLimitations.MaxColCount) then exit; { BIFF record header } @@ -2041,7 +2041,7 @@ procedure TsSpreadBIFFWriter.WriteNumber(AStream: TStream; var rec: TBIFF58NumberRecord; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; { BIFF Record header } @@ -2269,7 +2269,7 @@ var RPNLength: Word; RecordSizePos, FinalPos: Int64; begin - if (ARow >= FLimitations.MaxRows) or (ACol >= FLimitations.MaxCols) then + if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then exit; { BIFF Record header } @@ -2494,9 +2494,9 @@ var rowheight: Word; h: Single; begin - if (ARowIndex >= FLimitations.MaxRows) or - (AFirstColIndex >= FLimitations.MaxCols) or - (ALastColIndex >= FLimitations.MaxCols) + if (ARowIndex >= FLimitations.MaxRowCount) or + (AFirstColIndex >= FLimitations.MaxColCount) or + (ALastColIndex >= FLimitations.MaxColCount) then exit; diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas index 2aa118f1c..4694ca6ae 100755 --- a/components/fpspreadsheet/xlsxooxml.pas +++ b/components/fpspreadsheet/xlsxooxml.pas @@ -1857,9 +1857,11 @@ begin AppendToStream(AStream, ''); + GetSheetDimensions(AWorksheet, r1, r2, c1, c2); + if (boVirtualMode in Workbook.Options) and Assigned(Workbook.OnWriteCellData) then begin - for r := 0 to Workbook.VirtualRowCount-1 do begin + for r := 0 to r2 do begin row := AWorksheet.FindRow(r); if row <> nil then rh := Format(' ht="%g" customHeight="1"', [ @@ -1868,7 +1870,7 @@ begin rh := ''; AppendToStream(AStream, Format( '', [r+1, Workbook.VirtualColCount, rh])); - for c := 0 to Workbook.VirtualColCount-1 do begin + for c := 0 to c2 do begin InitCell(lCell); value := varNull; styleCell := nil; @@ -1905,6 +1907,7 @@ begin end else begin // The cells need to be written in order, row by row, cell by cell + (* c1 := AWorksheet.GetFirstColIndex; c2 := AWorksheet.GetLastColIndex; if (c1 = $FFFFFFFF) and (c2 = 0) then c1 := 0; // avoid arithmetic overflow in case of empty worksheet @@ -1912,6 +1915,7 @@ begin r2 := AWorksheet.GetlastRowIndex; if (r1 = $FFFFFFFF) and (r2 = 0) then r1 := 0; // avoid arithmetic overflow in case of empty worksheet // for r := 0 to AWorksheet.GetLastRowIndex do begin + *) for r := r1 to r2 do begin // If the row has a custom height add this value to the specification row := AWorksheet.FindRow(r); @@ -2298,8 +2302,8 @@ begin FPointSeparatorSettings.DecimalSeparator := '.'; // http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications - FLimitations.MaxCols := 16384; - FLimitations.MaxRows := 1048576; + FLimitations.MaxColCount := 16384; + FLimitations.MaxRowCount := 1048576; end; procedure TsSpreadOOXMLWriter.CreateNumFormatList;