From 36cc7d1f4ed6ac945140f569d1040dd83fd43746 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 18 Apr 2014 13:17:22 +0000 Subject: [PATCH] fpspreadsheet: Add support for reading/writing of column widths in biff8 (specified as multiples of the width of the character "0"). Add test case. No regressions git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2945 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/excel8demo/excel8read.lpi | 4 +- .../examples/excel8demo/excel8write.lpr | 6 +- .../examples/excel8demo/test.xls | Bin 6144 -> 6144 bytes components/fpspreadsheet/fpspreadsheet.pas | 4 +- .../fpspreadsheet/tests/formattests.pas | 53 ++++++++++++++ .../fpspreadsheet/tests/spreadtestgui.lpi | 6 +- .../fpspreadsheet/tests/testsutility.pas | 22 ++++++ components/fpspreadsheet/xlsbiff8.pas | 65 +++++++++++++++++- 8 files changed, 149 insertions(+), 11 deletions(-) diff --git a/components/fpspreadsheet/examples/excel8demo/excel8read.lpi b/components/fpspreadsheet/examples/excel8demo/excel8read.lpi index 43f419d9e..6dea16dd6 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8read.lpi +++ b/components/fpspreadsheet/examples/excel8demo/excel8read.lpi @@ -1,4 +1,4 @@ - + @@ -43,7 +43,7 @@ - + diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index ca5b5348f..a651b2de6 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -141,13 +141,13 @@ begin MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval); // Set width of columns 1 and 5 - lCol.Width := 100; //mm + lCol.Width := 30; MyWorksheet.WriteColInfo(1, lCol); - lCol.Width := 50; + lCol.Width := 5; MyWorksheet.WriteColInfo(5, lCol); // Set height of rows 5 and 6 - lRow.Height := 10; // mm + lRow.Height := 10; MyWorksheet.WriteRowInfo(5, lRow); lRow.Height := 5; MyWorksheet.WriteRowInfo(6, lRow); diff --git a/components/fpspreadsheet/examples/excel8demo/test.xls b/components/fpspreadsheet/examples/excel8demo/test.xls index a9d03e207fd924faf7ccc4c17e86def46e8b2002..301e0ee97ba54334cf1a1d88330d48de5c7606ac 100644 GIT binary patch delta 162 zcmZoLXfW9DgN3P(cQPYK+vWyV11`qBlQ;7FOV={+FfalkgB(8t2!QykK*+$#zxg-6 z8YAnQkN2HQCaVb=Kv~g(dQeuMpaqn5TF?f{;t_(ee1%M)Vr@c3P}W|!>^~tph}dRF KVR@E`feiq|gFQR| delta 138 zcmZoLXfW9DgM}%OcQPYK+vWyV11`q7lQ;7FZ&nacWMmC`EHy7~vYVg*lvORL2W71j uw1Bc+3)( - + @@ -157,8 +157,10 @@ - + + + diff --git a/components/fpspreadsheet/tests/testsutility.pas b/components/fpspreadsheet/tests/testsutility.pas index c592a7869..e564bf198 100644 --- a/components/fpspreadsheet/tests/testsutility.pas +++ b/components/fpspreadsheet/tests/testsutility.pas @@ -25,6 +25,9 @@ const // Returns an A.. notation based on sheet, row, optional column (e.g. A1). function CellNotation(WorkSheet: TsWorksheet; Row: integer; Column: integer=0): string; +// Returns an A notation of column based on sheed and column +function ColNotation(WorkSheet: TsWorksheet; Column:Integer): String; + // Note: using this function instead of GetWorkSheetByName for compatibility with // older fpspreadsheet versions that don't have that function function GetWorksheetByName(AWorkBook: TsWorkBook; AName: String): TsWorksheet; @@ -70,6 +73,25 @@ begin result:=WorkSheet.Name+'!'+inttostr(Column+1)+':'+inttostr(Row+1) end; +function ColNotation(WorkSheet: TsWorksheet; Column:Integer): String; +begin + if not Assigned(Worksheet) then + Result := 'ColNotation: error getting worksheet.' + else begin + if Column < 26 then + Result := Worksheet.Name + '!' + char(Column+65) + else + if Column < 26*26 then + Result := Worksheet.Name + '!' + char(Column div 26 + 65) + char(Column mod 26 + 65) + else + if Column < 26*26*26 then + Result := Worksheet.Name + '!' + char(Column div (26*26) + 65) + + char(Column mod (26*26) div 26 + 65) + + char(Column mod (26*26*26) + 65) + else + Result := 'ColNotation: At most three digits supported.'; + end; +end; end. diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 12995d471..530e01e25 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -117,6 +117,8 @@ type // procedure ReadCodepage in xlscommon // procedure ReadDateMode in xlscommon procedure ReadFont(const AStream: TStream); + // Read col info + procedure ReadColInfo(const AStream: TStream); public constructor Create; override; destructor Destroy; override; @@ -140,6 +142,7 @@ type procedure WriteXFFieldsForFormattingStyles(AStream: TStream); protected procedure AddDefaultFormats(); override; + procedure WriteColInfo(AStream: TStream; ASheet: TsWorksheet; ACol: PCol); public // constructor Create; // destructor Destroy; override; @@ -176,6 +179,7 @@ const { Excel record IDs } INT_EXCEL_ID_BOF = $0809; INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs + INT_EXCEL_ID_COLINFO = $007D; INT_EXCEL_ID_COUNTRY = $008C; INT_EXCEL_ID_EOF = $000A; INT_EXCEL_ID_DIMENSIONS = $0200; @@ -554,7 +558,9 @@ var MyData: TMemoryStream; CurrentPos: Int64; Boundsheets: array of Int64; - i, len: Integer; + sheet: TsWorksheet; + i, j, len: Integer; + col: PCol; begin { Write workbook globals } @@ -643,6 +649,8 @@ begin for i := 0 to AData.GetWorksheetCount - 1 do begin + sheet := AData.GetWorksheetByIndex(i); + { First goes back and writes the position of the BOF of the sheet on the respective BOUNDSHEET record } CurrentPos := AStream.Position; @@ -654,11 +662,16 @@ begin WriteIndex(AStream); - WriteDimensions(AStream, AData.GetWorksheetByIndex(i)); + for j := 0 to sheet.Cols.Count-1 do begin + col := PCol(sheet.Cols[j]); + WriteColInfo(AStream, sheet, col); + end; + + WriteDimensions(AStream, sheet); WriteWindow2(AStream, True); - WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).Cells); + WriteCellsToStream(AStream, sheet.Cells); WriteEOF(AStream); end; @@ -747,6 +760,32 @@ begin AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar)); end; +{******************************************************************* +* TsSpreadBIFF8Writer.WriteColumn () +* +* DESCRIPTION: Writes a COLINFO record +*******************************************************************} +procedure TsSpreadBIFF8Writer.WriteColInfo(AStream: TStream; + ASheet: TsWorkSheet; ACol: PCol); +var + w: Integer; +begin + if Assigned(ACol) and Assigned(ASheet) then begin + { BIFF Record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_COLINFO)); // BIFF record header + AStream.WriteWord(WordToLE(12)); // Record size + AStream.WriteWord(WordToLE(ACol^.Col)); // start column + AStream.WriteWord(WordToLE(ACol^.Col)); // end column + { calculate width to be in units of 1/256 of pixel width of character "0" } + w := round(ACol^.Width * 256); + AStream.WriteWord(WordToLE(w)); // write width + AStream.WriteWord(15); // XF record, ignored + AStream.WriteWord(0); // option flags, ignored + AStream.WriteWord(0); // "not used" + end; +end; + + {******************************************************************* * TsSpreadBIFF8Writer.WriteDateTime () * @@ -1868,6 +1907,7 @@ begin INT_EXCEL_ID_RK: ReadRKValue(AStream); INT_EXCEL_ID_MULRK: ReadMulRKValues(AStream); INT_EXCEL_ID_LABELSST:ReadLabelSST(AStream); //BIFF8 only + INT_EXCEL_ID_COLINFO: ReadColInfo(AStream); INT_EXCEL_ID_BOF: ; INT_EXCEL_ID_EOF: SectionEOF := True; else @@ -2384,6 +2424,25 @@ begin lFontName := ReadString(AStream, Len); end; +procedure TsSpreadBiff8Reader.ReadColInfo(const AStream: TStream); +var + c, c1, c2: Cardinal; + w: Word; + col: TCol; +begin + // read column start and end index of column range + c1 := WordLEToN(AStream.ReadWord); + c2 := WordLEToN(AStream.ReadWord); + // read col width in 1/256 of the width of "0" character + w := WordLEToN(AStream.ReadWord); + // calculate width in units of "characters" + col.Width := w / 256; + // assign width to columns + for c := c1 to c2 do + FWorksheet.WriteColInfo(c, col); +end; + + {******************************************************************* * Initialization section *