diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr index 9f6f595c7..27dba658f 100644 --- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr +++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr @@ -31,6 +31,8 @@ begin //MyWorksheet.WriteColWidth(0, 5); //MyWorksheet.WriteColWidth(1, 30); + MyWorksheet.WriteRowHeight(0, 30); // 30 mm + // Turn off grid lines and hide headers MyWorksheet.Options := MyWorksheet.Options - [soShowGridLines, soShowHeaders]; diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr index e7d1e2be4..7c3945b13 100644 --- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr +++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr @@ -43,6 +43,14 @@ begin MyWorkbook.AddFont('Calibri', 20, [], scRed); + // Change row height + MyWorksheet.WriteRowHeight(0, 20); // modify height of row 0 to 20 mm + + // Change colum widths + MyWorksheet.WriteColWidth(0, 40); + MyWorksheet.WriteColWidth(1, 20); + MyWorksheet.WriteColWidth(2, 20); + // Write some cells MyWorksheet.WriteNumber(0, 0, 1.0);// A1 MyWorksheet.WriteVertAlignment(0, 0, vaCenter); diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index 9667a2e49..2f11383c8 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -46,7 +46,7 @@ begin myWorksheet.LeftPaneWidth := 1; MyWorksheet.TopPaneHeight := 2; - { non-frozen panes not working, at the moment. Require SELECTION records? + { non-frozen panes not working, at the moment. Requires SELECTION records? MyWorksheet.LeftPaneWidth := 20*72*2; // 72 pt = inch --> 2 inches = 5 cm } @@ -122,15 +122,16 @@ begin MyWorksheet.WriteFont(8, 3, 'Courier New', 12, [fssUnderline], scBlue); MyWorksheet.WriteBackgroundColor(8, 3, scYellow); -{ Uncomment this to test large XLS files - for i := 2 to 20 do + + // Uncomment this to test large XLS files + for i := 40 to 1000 do begin - MyWorksheet.WriteAnsiText(i, 0, ParamStr(0)); - MyWorksheet.WriteAnsiText(i, 1, ParamStr(0)); - MyWorksheet.WriteAnsiText(i, 2, ParamStr(0)); - MyWorksheet.WriteAnsiText(i, 3, ParamStr(0)); +// MyWorksheet.WriteUTF8Text(i, 0, ParamStr(0)); +// MyWorksheet.WriteUTF8Text(i, 1, ParamStr(0)); +// MyWorksheet.WriteUTF8Text(i, 2, ParamStr(0)); + MyWorksheet.WriteUTF8Text(i, 3, ParamStr(0)); end; -} + // Write the formula E1 = A1 + B1 SetLength(MyRPNFormula, 3); @@ -209,17 +210,15 @@ begin MyWorksheet.WriteUTF8Text(37, 0, 'nfTimeInterval'); MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval); - // Set width of columns 1 and 5 - lCol.Width := 30; + // Set width of columns 0, 1 and 5 + MyWorksheet.WriteColWidth(0, 25); + lCol.Width := 20; MyWorksheet.WriteColInfo(1, lCol); lCol.Width := 5; MyWorksheet.WriteColInfo(5, lCol); - // Set height of rows 5 and 6 - lRow.Height := 10; - MyWorksheet.WriteRowInfo(5, lRow); - lRow.Height := 5; - MyWorksheet.WriteRowInfo(6, lRow); + // Set height of rows 0 + MyWorksheet.WriteRowHeight(0, 30); // 30 mm // Creates a new worksheet MyWorksheet := MyWorkbook.AddWorksheet(Str_Worksheet2); diff --git a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi index 57cce23ec..fb3d43d42 100644 --- a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi +++ b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi @@ -127,11 +127,10 @@ - - + @@ -139,7 +138,7 @@ - + @@ -149,10 +148,11 @@ + - - + + @@ -222,7 +222,6 @@ - @@ -230,7 +229,6 @@ - @@ -252,12 +250,10 @@ - - @@ -269,7 +265,7 @@ - + @@ -294,17 +290,17 @@ - + - - + + - + @@ -314,7 +310,7 @@ - + @@ -332,12 +328,10 @@ - - @@ -357,143 +351,139 @@ - - - - - - - - + + + + - + - + - - + + - + - - + + - + - + - - - - + + + + - + - + - + - + - + - + - + - + - + - + - + - - - - + + + + - + - - + + - + - + - + - - + + diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.lfm b/components/fpspreadsheet/examples/fpsgrid/mainform.lfm index ba4b318f4..ec5f4582e 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.lfm +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.lfm @@ -4,7 +4,7 @@ object Form1: TForm1 Top = 258 Width = 518 Caption = 'fpsGrid' - ClientHeight = 366 + ClientHeight = 361 ClientWidth = 518 Menu = MainMenu1 OnActivate = FormActivate @@ -12,19 +12,19 @@ object Form1: TForm1 LCLVersion = '1.3' object Panel1: TPanel Left = 0 - Height = 70 - Top = 296 + Height = 73 + Top = 288 Width = 518 Align = alBottom BevelOuter = bvNone - ClientHeight = 70 + ClientHeight = 73 ClientWidth = 518 TabOrder = 0 object btnPopulateGrid: TButton - Left = 400 - Height = 25 - Top = 13 - Width = 112 + Left = 392 + Height = 31 + Top = 24 + Width = 120 Caption = 'Populate Grid' OnClick = btnPopulateGridClick TabOrder = 0 @@ -32,9 +32,9 @@ object Form1: TForm1 end object CbShowHeaders: TCheckBox Left = 8 - Height = 19 - Top = 8 - Width = 93 + Height = 24 + Top = 11 + Width = 116 Caption = 'Show headers' Checked = True OnClick = CbShowHeadersClick @@ -43,9 +43,9 @@ object Form1: TForm1 end object CbShowGridLines: TCheckBox Left = 8 - Height = 19 - Top = 29 - Width = 100 + Height = 24 + Top = 36 + Width = 125 Caption = 'Show grid lines' Checked = True OnClick = CbShowGridLinesClick @@ -53,35 +53,35 @@ object Form1: TForm1 TabOrder = 2 end object EdFrozenCols: TSpinEdit - Left = 208 - Height = 23 + Left = 238 + Height = 28 Top = 8 Width = 52 OnChange = EdFrozenColsChange TabOrder = 3 end object EdFrozenRows: TSpinEdit - Left = 208 - Height = 23 - Top = 36 + Left = 238 + Height = 28 + Top = 39 Width = 52 OnChange = EdFrozenRowsChange TabOrder = 4 end object Label1: TLabel - Left = 135 - Height = 15 + Left = 152 + Height = 20 Top = 13 - Width = 62 + Width = 77 Caption = 'Frozen cols:' FocusControl = EdFrozenCols ParentColor = False end object Label2: TLabel - Left = 136 - Height = 15 - Top = 39 - Width = 66 + Left = 153 + Height = 20 + Top = 40 + Width = 82 Caption = 'Frozen rows:' FocusControl = EdFrozenRows ParentColor = False @@ -89,7 +89,7 @@ object Form1: TForm1 end object PageControl1: TPageControl Left = 0 - Height = 270 + Height = 262 Top = 26 Width = 518 ActivePage = TabSheet1 @@ -99,17 +99,24 @@ object Form1: TForm1 OnChange = PageControl1Change object TabSheet1: TTabSheet Caption = 'Sheet1' - ClientHeight = 242 + ClientHeight = 229 ClientWidth = 510 object sWorksheetGrid1: TsWorksheetGrid Left = 0 - Height = 242 + Height = 229 Top = 0 Width = 510 Align = alClient + ColCount = 2 + ExtendedSelect = False Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goThumbTracking, goSmoothScroll, goFixedColSizing] + RowCount = 2 TabOrder = 0 TitleStyle = tsNative + ColWidths = ( + 56 + 64 + ) end end end diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.lrs b/components/fpspreadsheet/examples/fpsgrid/mainform.lrs index 65910124a..c59c1159a 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.lrs +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.lrs @@ -2,37 +2,38 @@ LazarusResources.Add('TForm1','FORMDATA',[ 'TPF0'#6'TForm1'#5'Form1'#4'Left'#3'r'#1#6'Height'#3#130#1#3'Top'#3#2#1#5'Wid' - +'th'#3#6#2#7'Caption'#6#7'fpsGrid'#12'ClientHeight'#3'n'#1#11'ClientWidth'#3 + +'th'#3#6#2#7'Caption'#6#7'fpsGrid'#12'ClientHeight'#3'i'#1#11'ClientWidth'#3 +#6#2#4'Menu'#7#9'MainMenu1'#10'OnActivate'#7#12'FormActivate'#8'ShowHint'#9 - +#10'LCLVersion'#6#3'1.3'#0#6'TPanel'#6'Panel1'#4'Left'#2#0#6'Height'#2'F'#3 - +'Top'#3'('#1#5'Width'#3#6#2#5'Align'#7#8'alBottom'#10'BevelOuter'#7#6'bvNone' - +#12'ClientHeight'#2'F'#11'ClientWidth'#3#6#2#8'TabOrder'#2#0#0#7'TButton'#15 - +'btnPopulateGrid'#4'Left'#3#144#1#6'Height'#2#25#3'Top'#2#13#5'Width'#2'p'#7 + +#10'LCLVersion'#6#3'1.3'#0#6'TPanel'#6'Panel1'#4'Left'#2#0#6'Height'#2'I'#3 + +'Top'#3' '#1#5'Width'#3#6#2#5'Align'#7#8'alBottom'#10'BevelOuter'#7#6'bvNone' + +#12'ClientHeight'#2'I'#11'ClientWidth'#3#6#2#8'TabOrder'#2#0#0#7'TButton'#15 + +'btnPopulateGrid'#4'Left'#3#136#1#6'Height'#2#31#3'Top'#2#24#5'Width'#2'x'#7 +'Caption'#6#13'Populate Grid'#7'OnClick'#7#20'btnPopulateGridClick'#8'TabOrd' +'er'#2#0#7'Visible'#8#0#0#9'TCheckBox'#13'CbShowHeaders'#4'Left'#2#8#6'Heigh' - +'t'#2#19#3'Top'#2#8#5'Width'#2']'#7'Caption'#6#12'Show headers'#7'Checked'#9 + +'t'#2#24#3'Top'#2#11#5'Width'#2't'#7'Caption'#6#12'Show headers'#7'Checked'#9 +#7'OnClick'#7#18'CbShowHeadersClick'#5'State'#7#9'cbChecked'#8'TabOrder'#2#1 - +#0#0#9'TCheckBox'#15'CbShowGridLines'#4'Left'#2#8#6'Height'#2#19#3'Top'#2#29 - +#5'Width'#2'd'#7'Caption'#6#15'Show grid lines'#7'Checked'#9#7'OnClick'#7#20 + +#0#0#9'TCheckBox'#15'CbShowGridLines'#4'Left'#2#8#6'Height'#2#24#3'Top'#2'$' + +#5'Width'#2'}'#7'Caption'#6#15'Show grid lines'#7'Checked'#9#7'OnClick'#7#20 +'CbShowGridLinesClick'#5'State'#7#9'cbChecked'#8'TabOrder'#2#2#0#0#9'TSpinEd' - +'it'#12'EdFrozenCols'#4'Left'#3#208#0#6'Height'#2#23#3'Top'#2#8#5'Width'#2'4' + +'it'#12'EdFrozenCols'#4'Left'#3#238#0#6'Height'#2#28#3'Top'#2#8#5'Width'#2'4' +#8'OnChange'#7#18'EdFrozenColsChange'#8'TabOrder'#2#3#0#0#9'TSpinEdit'#12'Ed' - +'FrozenRows'#4'Left'#3#208#0#6'Height'#2#23#3'Top'#2'$'#5'Width'#2'4'#8'OnCh' - +'ange'#7#18'EdFrozenRowsChange'#8'TabOrder'#2#4#0#0#6'TLabel'#6'Label1'#4'Le' - +'ft'#3#135#0#6'Height'#2#15#3'Top'#2#13#5'Width'#2'>'#7'Caption'#6#12'Frozen' - +' cols:'#12'FocusControl'#7#12'EdFrozenCols'#11'ParentColor'#8#0#0#6'TLabel' - +#6'Label2'#4'Left'#3#136#0#6'Height'#2#15#3'Top'#2''''#5'Width'#2'B'#7'Capti' - +'on'#6#12'Frozen rows:'#12'FocusControl'#7#12'EdFrozenRows'#11'ParentColor'#8 - +#0#0#0#12'TPageControl'#12'PageControl1'#4'Left'#2#0#6'Height'#3#14#1#3'Top' - +#2#26#5'Width'#3#6#2#10'ActivePage'#7#9'TabSheet1'#5'Align'#7#8'alClient'#8 - +'TabIndex'#2#0#8'TabOrder'#2#1#8'OnChange'#7#18'PageControl1Change'#0#9'TTab' - +'Sheet'#9'TabSheet1'#7'Caption'#6#6'Sheet1'#12'ClientHeight'#3#242#0#11'Clie' - +'ntWidth'#3#254#1#0#15'TsWorksheetGrid'#15'sWorksheetGrid1'#4'Left'#2#0#6'He' - +'ight'#3#242#0#3'Top'#2#0#5'Width'#3#254#1#5'Align'#7#8'alClient'#7'Options' - +#11#15'goFixedVertLine'#15'goFixedHorzLine'#10'goVertLine'#10'goHorzLine'#13 - +'goRangeSelect'#11'goRowSizing'#11'goColSizing'#15'goThumbTracking'#14'goSmo' - +'othScroll'#16'goFixedColSizing'#0#8'TabOrder'#2#0#10'TitleStyle'#7#8'tsNati' - +'ve'#0#0#0#0#8'TToolBar'#8'ToolBar1'#4'Left'#2#0#6'Height'#2#26#3'Top'#2#0#5 + +'FrozenRows'#4'Left'#3#238#0#6'Height'#2#28#3'Top'#2''''#5'Width'#2'4'#8'OnC' + +'hange'#7#18'EdFrozenRowsChange'#8'TabOrder'#2#4#0#0#6'TLabel'#6'Label1'#4'L' + +'eft'#3#152#0#6'Height'#2#20#3'Top'#2#13#5'Width'#2'M'#7'Caption'#6#12'Froze' + +'n cols:'#12'FocusControl'#7#12'EdFrozenCols'#11'ParentColor'#8#0#0#6'TLabel' + +#6'Label2'#4'Left'#3#153#0#6'Height'#2#20#3'Top'#2'('#5'Width'#2'R'#7'Captio' + +'n'#6#12'Frozen rows:'#12'FocusControl'#7#12'EdFrozenRows'#11'ParentColor'#8 + +#0#0#0#12'TPageControl'#12'PageControl1'#4'Left'#2#0#6'Height'#3#6#1#3'Top'#2 + +#26#5'Width'#3#6#2#10'ActivePage'#7#9'TabSheet1'#5'Align'#7#8'alClient'#8'Ta' + +'bIndex'#2#0#8'TabOrder'#2#1#8'OnChange'#7#18'PageControl1Change'#0#9'TTabSh' + +'eet'#9'TabSheet1'#7'Caption'#6#6'Sheet1'#12'ClientHeight'#3#229#0#11'Client' + +'Width'#3#254#1#0#15'TsWorksheetGrid'#15'sWorksheetGrid1'#4'Left'#2#0#6'Heig' + +'ht'#3#229#0#3'Top'#2#0#5'Width'#3#254#1#5'Align'#7#8'alClient'#8'ColCount'#2 + +#2#14'ExtendedSelect'#8#7'Options'#11#15'goFixedVertLine'#15'goFixedHorzLine' + +#10'goVertLine'#10'goHorzLine'#13'goRangeSelect'#11'goRowSizing'#11'goColSiz' + +'ing'#15'goThumbTracking'#14'goSmoothScroll'#16'goFixedColSizing'#0#8'RowCou' + +'nt'#2#2#8'TabOrder'#2#0#10'TitleStyle'#7#8'tsNative'#9'ColWidths'#1#2'8'#2 + +'@'#0#0#0#0#0#8'TToolBar'#8'ToolBar1'#4'Left'#2#0#6'Height'#2#26#3'Top'#2#0#5 +'Width'#3#6#2#12'ButtonHeight'#2#24#7'Caption'#6#8'ToolBar1'#11'EdgeBorders' +#11#0#6'Images'#7#10'ImageList1'#8'TabOrder'#2#2#0#11'TToolButton'#11'ToolBu' +'tton1'#4'Left'#2#1#3'Top'#2#0#6'Action'#7#6'AcOpen'#0#0#11'TToolButton'#11 @@ -64,8 +65,8 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#248#255#137#227#248#255#130#225#247#255'z'#223#247#255'q'#222#246#255'g'#219 +#245#255'['#216#244#255'M'#212#243#255'@'#209#242#255#202#242#251#255'5'#148 +#218#255#255#255#255#0#255#255#255#0'6'#154#218#248#242#250#253#255#148#230 - +#248#255#146#229#248#255#144#229#248#255#139#227#248#255#134#226#247#255#127 - ,#225#247#255'w'#222#246#255'l'#220#246#255'^'#217#244#255'O'#213#243#255#204 + ,#248#255#146#229#248#255#144#229#248#255#139#227#248#255#134#226#247#255#127 + +#225#247#255'w'#222#246#255'l'#220#246#255'^'#217#244#255'O'#213#243#255#204 +#242#251#255'5'#148#218#255#255#255#255#0#255#255#255#0'6'#161#218#249#246 +#252#254#255#148#229#248#255#147#229#248#255#147#229#248#255#145#229#248#255 +#147#219#233#255#147#215#227#255#147#210#220#255#144#206#215#255#140#200#207 @@ -128,8 +129,8 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248 +#255#254#251#248#255#254#251#248#255#254#251#248#255#211#150'm'#255#210#167 +#138#255#171'b2'#255#187'j6'#255#240#210#190#255#226#163'z'#255#226#163'z' - +#255#225#163'z'#255#226#163'{'#255#225#163'{'#255#224#161'x'#255#222#159'w' - ,#255#221#159'v'#255#220#157't'#255#217#155'r'#255#216#153'q'#255#214#153'p' + ,#255#225#163'z'#255#226#163'{'#255#225#163'{'#255#224#161'x'#255#222#159'w' + +#255#221#159'v'#255#220#157't'#255#217#155'r'#255#216#153'q'#255#214#153'p' +#255#213#171#142#255#173'c3'#255#187'j6'#255#242#213#194#255#227#163'z'#255 +#227#163'z'#255#226#163'{'#255#226#163'{'#255#226#164'{'#255#225#162'y'#255 +#224#161'x'#255#222#160'w'#255#222#158'u'#255#220#157't'#255#218#155's'#255 @@ -192,8 +193,8 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#145'K'#255'?'#141'H'#255'='#137'E'#255']'#164'e'#255'Z'#160'a'#255'E'#131'K' +#255#158#158#158#255#158#158#158#255'`'#146#201#255#158#199#226#255#131#184 +#218#255'}'#180#215#255'~'#179#215#255'O'#137#180#255';y'#177#234#255#255#255 - +#0#255#255#255#0'www'#255#154#154#154#255'='#138'E'#255'I'#138'O'#255#156#156 - ,#156#255#157#157#157#255#157#157#157#255'f'#150#204#255#162#203#227#255#137 + ,#0#255#255#255#0'www'#255#154#154#154#255'='#138'E'#255'I'#138'O'#255#156#156 + +#156#255#157#157#157#255#157#157#157#255'f'#150#204#255#162#203#227#255#137 +#189#220#255#131#185#218#255#132#185#218#255'Q'#139#181#255'C~'#182#234#255 +#255#255#0#255#255#255#0'zzz'#255#153#153#153#255'R'#145'Y'#255#153#154#153 +#255#155#155#155#255#156#156#156#255#156#156#156#255'l'#154#208#255#167#206 @@ -256,8 +257,8 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#255'l'#220#246#255'^'#217#244#255'O'#213#243#255#204#242#251#255'5'#148#218 +#255#255#255#255#0#255#255#255#0'5'#148#218#247#239#250#254#255#147#229#248 +#255#143#228#248#255#137#227#248#255#130#225#247#255'z'#223#247#255'q'#222 - +#246#255'g'#219#245#255'['#216#244#255'M'#212#243#255'@'#209#242#255#202#242 - ,#251#255'5'#148#218#255#255#255#255#0#255#255#255#0'3'#142#217#251#220#240 + ,#246#255'g'#219#245#255'['#216#244#255'M'#212#243#255'@'#209#242#255#202#242 + +#251#255'5'#148#218#255#255#255#255#0#255#255#255#0'3'#142#217#251#220#240 +#250#255#152#225#246#255#149#224#246#255#146#223#246#255#142#222#245#255#137 +#220#245#255#133#218#244#255#128#217#244#255'z'#215#243#255't'#213#243#255'p' +#211#242#255#194#234#248#255'5'#148#218#255#255#255#255#0#255#255#255#0',' @@ -320,8 +321,8 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'p'#158#214#219 +'m'#156#212#255#133#177#218#255'Z'#145#185#255'`'#147#203#234#255#255#255#0 - +#255#255#255#0#128#128#128#255'~~~'#255'|||'#255'zzz'#255'www'#255'uuu'#255 - ,'rrr'#255'q'#158#212#255'o'#158#214#255#135#178#220#255#171#211#232#255#169 + ,#255#255#255#0#128#128#128#255'~~~'#255'|||'#255'zzz'#255'www'#255'uuu'#255 + +'rrr'#255'q'#158#212#255'o'#158#214#255#135#178#220#255#171#211#232#255#169 +#208#230#255'X'#144#184#255'Y'#142#198#234#255#255#255#0#255#255#255#0'}}}' +#255#153#153#153#255#153#153#153#255#154#154#154#255#154#154#154#255#155#155 +#155#255#155#155#155#255'o'#157#211#255#170#209#231#255#171#209#231#255#152 diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index fadb034f2..cd056e275 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -290,7 +290,6 @@ type TRow = record Row: Cardinal; Height: Single; // in millimeters - AutoHeight: Boolean; // true: height corresponds to font; false: use Height end; PRow = ^TRow; @@ -337,6 +336,8 @@ type function GetCellCount: Cardinal; function GetFirstCell(): PCell; function GetNextCell(): PCell; + function GetFirstCellOfRow(ARow: Cardinal): PCell; + function GetLastCellOfRow(ARow: Cardinal): PCell; function GetLastColNumber: Cardinal; function GetLastRowNumber: Cardinal; function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; @@ -385,6 +386,7 @@ type procedure RemoveAllRows; procedure RemoveAllCols; procedure WriteRowInfo(ARow: Cardinal; AData: TRow); + procedure WriteRowHeight(ARow: Cardinal; AHeight: Single); procedure WriteColInfo(ACol: Cardinal; AData: TCol); procedure WriteColWidth(ACol: Cardinal; AWidth: Single); { Properties } @@ -1014,6 +1016,32 @@ begin end; end; +function TsWorksheet.GetFirstCellOfRow(ARow: Cardinal): PCell; +var + c, n: Cardinal; +begin + n := GetLastColNumber; + c := 0; + Result := FindCell(ARow, c); + while (result = nil) and (c < n) do begin + inc(c); + result := FindCell(ARow, c); + end; +end; + +function TsWorksheet.GetLastCellOfRow(ARow: Cardinal): PCell; +var + c, n: Cardinal; +begin + n := GetLastColNumber; + c := n; + Result := FindCell(ARow, c); + while (Result = nil) and (c > 0) do begin + dec(c); + Result := FindCell(ARow, c); + end; +end; + {@@ Returns the 0-based number of the last row with a cell with contents. @@ -1624,14 +1652,10 @@ end; function TsWorksheet.GetRow(ARow: Cardinal): PRow; begin Result := FindRow(ARow); - - if (Result = nil) then - begin + if (Result = nil) then begin Result := GetMem(SizeOf(TRow)); FillChar(Result^, SizeOf(TRow), #0); - Result^.Row := ARow; - FRows.Add(Result); end; end; @@ -1639,14 +1663,10 @@ end; function TsWorksheet.GetCol(ACol: Cardinal): PCol; begin Result := FindCol(ACol); - - if (Result = nil) then - begin + if (Result = nil) then begin Result := GetMem(SizeOf(TCol)); FillChar(Result^, SizeOf(TCol), #0); - Result^.Col := ACol; - FCols.Add(Result); end; end; @@ -1656,8 +1676,7 @@ var Node: Pointer; i: Integer; begin - for i := FRows.Count-1 downto 0 do - begin + for i := FRows.Count-1 downto 0 do begin Node := FRows.Items[i]; FreeMem(Node, SizeOf(TRow)); end; @@ -1669,8 +1688,7 @@ var Node: Pointer; i: Integer; begin - for i := FCols.Count-1 downto 0 do - begin + for i := FCols.Count-1 downto 0 do begin Node := FCols.Items[i]; FreeMem(Node, SizeOf(TCol)); end; @@ -1685,6 +1703,14 @@ begin AElement^.Height := AData.Height; end; +procedure TsWorksheet.WriteRowHeight(ARow: Cardinal; AHeight: Single); +var + AElement: PRow; +begin + AElement := GetRow(ARow); + AElement^.Height := AHeight; +end; + procedure TsWorksheet.WriteColInfo(ACol: Cardinal; AData: TCol); var AElement: PCol; diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index 6bc469314..ddce9c575 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -876,7 +876,7 @@ begin end; for i := FHeaderCount to RowCount-1 do begin lRow := FWorksheet.FindRow(i - FHeaderCount); - if (lRow = nil) or lRow^.AutoHeight then + if (lRow = nil) then RowHeights[i] := CalcAutoRowHeight(i) else RowHeights[i] := CalcRowHeight(lRow^.Height); diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas index 75f4354ac..a61e0a934 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas +++ b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas @@ -535,7 +535,7 @@ begin s := '$0005=WB globals, $0006=VB module, ' + s + ', $0100=Workspace'; w := WordLEToN(w); if Row = FCurrRow then begin - FDetails.Add('Type of data:'); + FDetails.Add('Type of data:'#13); FDetails.Add(Format('$%.4x = %s', [w, BofName(w)])); end; ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), @@ -906,7 +906,6 @@ begin ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)), 'Index to first used row'); - numBytes := 4; Move(FBuffer[FBufferIndex], dw, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)), 'Index to last used row, increased by 1'); @@ -916,7 +915,6 @@ begin ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first used row'); - numBytes := 2; Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last used row, increased by 1'); @@ -932,9 +930,11 @@ begin ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last used column, increased by 1'); - numBytes := 2; - ShowInRow(FCurrRow, FBufferIndex, numBytes, '', - '(not used)'); + if FFormat <> sfExcel2 then begin + numBytes := 2; + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', + '(not used)'); + end; end; @@ -2714,7 +2714,17 @@ begin numBytes := 2; Move(FBuffer[FBufferIndex], w, numbytes); - ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [WordLEToN(w)]), + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Row height:'#13); + FDetails.Add(Format('Bits 14-0 = %d: Row height in twips (1/20 pt) --> %.1f-pt', + [w and $7FFF, (w and $7FFF)/20.0]) + ); + if w and $8000 = 0 + then FDetails.Add('Bit 15 = 0: Row has custom height') + else FDetails.Add('Bit 15 = 1: Row has default height'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), 'Bits 14-0: Height of row in twips (1/20 pt), Bit 15: Row has default height'); numBytes := 2; diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas b/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas index 9d71f403a..f2dbcbd24 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas +++ b/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas @@ -720,7 +720,6 @@ end; procedure TMainForm.LoadFile(const AFileName: String); var - fmt: TsSpreadsheetFormat; valid: Boolean; excptn: Exception = nil; begin @@ -735,19 +734,19 @@ begin exit; end; - fmt := sfExcel8; + FFormat := sfExcel8; while True do begin try - LoadFile(AFileName, fmt); + LoadFile(AFileName, FFormat); valid := True; except on E: Exception do begin - if fmt = sfExcel8 then excptn := E; + if FFormat = sfExcel8 then excptn := E; valid := False end; end; - if valid or (fmt = sfExcel2) then Break; - fmt := Pred(fmt); + if valid or (FFormat = sfExcel2) then Break; + FFormat := Pred(FFormat); end; // A failed attempt to read a file should bring an exception, so re-raise @@ -755,8 +754,6 @@ begin // since this is the most common format if (not valid) and (excptn <> nil) then raise excptn; - - FFormat := fmt; end; @@ -793,7 +790,9 @@ begin FFileName := ExpandFileName(AFileName); ReadFromStream(MemStream); + FFormat := AFormat; UpdateCaption; + FMRUMenuManager.AddToRecent(AFileName); end; @@ -1062,9 +1061,9 @@ begin while AStream.Position < AStream.Size do begin p := AStream.Position; recType := WordLEToN(AStream.ReadWord); - if recType = 0 then - break; recSize := WordLEToN(AStream.ReadWord); + if (recType = 0) and (recSize = 0) then + break; s := RecTypeName(recType); i := pos(':', s); // in case of BOF record: create new parent node for this substream diff --git a/components/fpspreadsheet/tests/formattests.pas b/components/fpspreadsheet/tests/formattests.pas index a3da9ffe0..c0406372d 100644 --- a/components/fpspreadsheet/tests/formattests.pas +++ b/components/fpspreadsheet/tests/formattests.pas @@ -29,6 +29,7 @@ var SollDateTimeFormatStrings: array[0..9] of String; SollColWidths: array[0..1] of Single; + SollRowHeights: Array[0..2] of Single; SollBorders: array[0..15] of TsCellBorders; SollBorderLineStyles: array[0..6] of TsLineStyle; SollBorderColors: array[0..5] of TsColor; @@ -53,6 +54,8 @@ type procedure TestWriteReadBorderStyles(AFormat: TsSpreadsheetFormat); // Test column widths procedure TestWriteReadColWidths(AFormat: TsSpreadsheetFormat); + // Test row heights + procedure TestWriteReadRowHeights(AFormat: TsSpreadsheetFormat); // Test text rotation procedure TestWriteReadTextRotation(AFormat:TsSpreadsheetFormat); // Test word wrapping @@ -70,6 +73,7 @@ type procedure TestWriteReadBIFF2_Alignment; procedure TestWriteReadBIFF2_Border; procedure TestWriteReadBIFF2_ColWidths; + procedure TestWriteReadBIFF2_RowHeights; procedure TestWriteReadBIFF2_DateTimeFormats; procedure TestWriteReadBIFF2_NumberFormats; // These features are not supported by Excel2 --> no test cases required! @@ -82,6 +86,7 @@ type procedure TestWriteReadBIFF5_Border; procedure TestWriteReadBIFF5_BorderStyles; procedure TestWriteReadBIFF5_ColWidths; + procedure TestWriteReadBIFF5_RowHeights; procedure TestWriteReadBIFF5_DateTimeFormats; procedure TestWriteReadBIFF5_NumberFormats; procedure TestWriteReadBIFF5_TextRotation; @@ -92,6 +97,7 @@ type procedure TestWriteReadBIFF8_Border; procedure TestWriteReadBIFF8_BorderStyles; procedure TestWriteReadBIFF8_ColWidths; + procedure TestWriteReadBIFF8_RowHeights; procedure TestWriteReadBIFF8_DateTimeFormats; procedure TestWriteReadBIFF8_NumberFormats; procedure TestWriteReadBIFF8_TextRotation; @@ -101,12 +107,13 @@ type implementation uses - TypInfo; + TypInfo, fpsutils; const FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet FmtDateTimesSheet = 'DateTimesFormat'; ColWidthSheet = 'ColWidths'; + RowHeightSheet = 'RowHeights'; BordersSheet = 'CellBorders'; AlignmentSheet = 'TextAlignments'; TextRotationSheet = 'TextRotation'; @@ -189,6 +196,11 @@ begin SollColWidths[0] := 20; // characters based on width of "0" SollColWidths[1] := 40; + // Row heights + SollRowHeights[0] := 5; + SollRowHeights[1] := 10; + SollRowHeights[2] := 50; + // Cell borders SollBorders[0] := []; SollBorders[1] := [cbEast]; @@ -672,7 +684,8 @@ begin if lpCol = nil then fail('Error in test code. Failed to return saved column width'); ActualColWidth := lpCol^.Width; - CheckEquals(SollColWidths[Col], ActualColWidth, 'Test saved colwidth mismatch column '+ColNotation(MyWorkSheet,Col)); + CheckEquals(SollColWidths[Col], ActualColWidth, + 'Test saved colwidth mismatch, column '+ColNotation(MyWorkSheet,Col)); end; // Finalization MyWorkbook.Free; @@ -695,6 +708,68 @@ begin TestWriteReadColWidths(sfExcel8); end; +procedure TSpreadWriteReadFormatTests.TestWriteReadRowHeights(AFormat: TsSpreadsheetFormat); +var + MyWorksheet: TsWorksheet; + MyWorkbook: TsWorkbook; + ActualRowHeight: Single; + Row: Integer; + lpRow: PRow; + TempFile: string; //write xls/xml to this file and read back from it +begin + TempFile:=GetTempFileName; + {// Not needed: use workbook.writetofile with overwrite=true + if fileexists(TempFile) then + DeleteFile(TempFile); + } + // Write out all test values + MyWorkbook := TsWorkbook.Create; + MyWorkSheet:= MyWorkBook.AddWorksheet(RowHeightSheet); + for Row := Low(SollRowHeights) to High(SollRowHeights) do + MyWorksheet.WriteRowHeight(Row, SollRowHeights[Row]); + MyWorkBook.WriteToFile(TempFile, AFormat, true); + MyWorkbook.Free; + + // Open the spreadsheet, as biff8 + MyWorkbook := TsWorkbook.Create; + MyWorkbook.ReadFromFile(TempFile, AFormat); + if AFormat = sfExcel2 then + MyWorksheet := MyWorkbook.GetFirstWorksheet + else + MyWorksheet := GetWorksheetByName(MyWorkBook, RowHeightSheet); + if MyWorksheet=nil then + fail('Error in test code. Failed to get named worksheet'); + for Row := Low(SollRowHeights) to High(SollRowHeights) do begin + lpRow := MyWorksheet.GetRow(Row); + if lpRow = nil then + fail('Error in test code. Failed to return saved row height'); + // Rounding to twips in Excel would cause severe rounding error if we'd compare millimeters + // --> go back to twips + ActualRowHeight := MillimetersToTwips(lpRow^.Height); + CheckEquals(MillimetersToTwips(SollRowHeights[Row]), ActualRowHeight, + 'Test saved row height mismatch, row '+RowNotation(MyWorkSheet,Row)); + end; + // Finalization + MyWorkbook.Free; + + DeleteFile(TempFile); +end; + +procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF2_RowHeights; +begin + TestWriteReadRowHeights(sfExcel2); +end; + +procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF5_RowHeights; +begin + TestWriteReadRowHeights(sfExcel5); +end; + +procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF8_RowHeights; +begin + TestWriteReadRowHeights(sfExcel8); +end; + procedure TSpreadWriteReadFormatTests.TestWriteReadTextRotation(AFormat: TsSpreadsheetFormat); const col = 0; diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index 1f365e8f6..ddd31284d 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -37,7 +37,6 @@ - diff --git a/components/fpspreadsheet/tests/testsutility.pas b/components/fpspreadsheet/tests/testsutility.pas index 9d31c9bb9..2418881cf 100644 --- a/components/fpspreadsheet/tests/testsutility.pas +++ b/components/fpspreadsheet/tests/testsutility.pas @@ -28,6 +28,9 @@ function CellNotation(WorkSheet: TsWorksheet; Row: integer; Column: integer=0): // Returns an A notation of column based on sheet and column function ColNotation(WorkSheet: TsWorksheet; Column:Integer): String; +// Returns a notation for row bassed on sheet and row +function RowNotation(Worksheet: TsWorksheet; Row: 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; @@ -97,5 +100,13 @@ begin Result := WorkSheet.Name + '!' + ColumnToLetter(Column); end; +function RowNotation(Worksheet: TsWorksheet; Row: Integer): String; +begin + if not Assigned(Worksheet) then + Result := 'RowNotation: error getting worksheet.' + else + Result := Worksheet.Name + '!' + IntToStr(Row+1); +end; + end. diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index d8c52dbcb..014d67267 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -89,13 +89,15 @@ type AddBackground: Boolean = false); procedure WriteXFFieldsForFormattingStyles(AStream: TStream); procedure WriteXFRecords(AStream: TStream); - procedure WriteWindow1(AStream: TStream); override; - procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet); protected procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override; procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; + procedure WriteRow(AStream: TStream; ASheet: TsWorksheet; + ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); override; + procedure WriteWindow1(AStream: TStream); override; + procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet); public { General writing methods } procedure WriteToStream(AStream: TStream); override; @@ -155,10 +157,9 @@ const INT_EXCEL_ID_INTEGER = $0002; INT_EXCEL_ID_NUMBER = $0003; INT_EXCEL_ID_LABEL = $0004; - INT_EXCEL_ID_FORMULA = $0006; - INT_EXCEL_ID_ROWINFO = $0008; + INT_EXCEL_ID_ROW = $0008; INT_EXCEL_ID_BOF = $0009; - INT_EXCEL_ID_EOF = $000A; + INT_EXCEL_ID_INDEX = $000B; INT_EXCEL_ID_FORMAT = $001E; INT_EXCEL_ID_FORMATCOUNT= $001F; INT_EXCEL_ID_COLWIDTH = $0024; @@ -427,7 +428,7 @@ begin INT_EXCEL_ID_LABEL : ReadLabel(AStream); INT_EXCEL_ID_FORMULA : ReadFormula(AStream); INT_EXCEL_ID_COLWIDTH : ReadColWidth(AStream); - INT_EXCEL_ID_ROWINFO : ReadRowInfo(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream); INT_EXCEL_ID_XF : ReadXF(AStream); @@ -565,8 +566,7 @@ begin lRow := FWorksheet.GetRow(WordLEToN(rowrec.RowIndex)); // Row height is encoded into the 15 remaining bits in units "twips" (1/20 pt) lRow^.Height := TwipsToMillimeters(h and $7FFF); - end else - lRow^.AutoHeight := true; + end; end; { Reads the WINDOW2 record containing information like "show grid lines", @@ -828,6 +828,7 @@ begin WriteFormats(AStream); WriteXFRecords(AStream); WriteColWidths(AStream); + WriteRows(AStream, sheet); WriteCellsToStream(AStream, sheet.Cells); WriteWindow1(AStream); @@ -1533,6 +1534,55 @@ begin AStream.WriteBuffer(AValue, 8); end; +procedure TsSpreadBIFF2Writer.WriteRow(AStream: TStream; ASheet: TsWorksheet; + ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); +var + containsXF: Boolean; + rowheight: Word; + w: Word; +begin + containsXF := false; + + { BIFF record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_ROW)); + AStream.WriteWord(WordToLE(IfThen(containsXF, 18, 13))); + + { Index of row } + AStream.WriteWord(WordToLE(Word(ARowIndex))); + + { Index to column of the first cell which is described by a cell record } + AStream.WriteWord(WordToLE(Word(AFirstColIndex))); + + { Index to column of the last cell which is described by a cell record, increased by 1 } + AStream.WriteWord(WordToLE(Word(ALastColIndex) + 1)); + + { Row height (in twips, 1/20 point) and info on custom row height } + if (ARow = nil) or (ARow^.Height = 0) then + rowheight := round(Workbook.GetFont(0).Size*20) + else + rowheight := MillimetersToTwips(ARow^.Height); + w := rowheight and $7FFF; + AStream.WriteWord(WordToLE(w)); + + { not used } + AStream.WriteWord(0); + + { Contains row attribute field and XF index } + AStream.WriteByte(ord(containsXF)); + + { Relative offset to calculate stream position of the first cell record for this row } + AStream.WriteWord(0); + + if containsXF then begin + { Default row attributes } + AStream.WriteByte(0); + AStream.WriteByte(0); + AStream.WriteByte(0); + + { Index to XF record } + AStream.WriteWord(WordToLE(15)); + end; +end; {******************************************************************* * Initialization section diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index 36fcd8f22..b18a103f1 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -219,20 +219,7 @@ implementation 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_EOF = $000A; - INT_EXCEL_ID_DIMENSIONS = $0200; - INT_EXCEL_ID_FORMAT = $041E; - INT_EXCEL_ID_FORMULA = $0006; - INT_EXCEL_ID_INDEX = $020B; - INT_EXCEL_ID_ROWINFO = $0208; - INT_EXCEL_ID_STYLE = $0293; - INT_EXCEL_ID_WINDOW1 = $003D; - INT_EXCEL_ID_RSTRING = $00D6; - INT_EXCEL_ID_RK = $027E; - INT_EXCEL_ID_MULRK = $00BD; - INT_EXCEL_ID_CODEPAGE = xlscommon.INT_EXCEL_ID_CODEPAGE; + // see: in xlscommon { Cell Addresses constants } MASK_EXCEL_ROW = $3FFF; @@ -412,6 +399,7 @@ begin WriteDimensions(AStream, sheet); WriteWindow2(AStream, sheet); WritePane(AStream, sheet, true); // true for "is BIFF5 or BIFF8" + WriteRows(AStream, sheet); WriteCellsToStream(AStream, sheet.Cells); WriteEOF(AStream); end; @@ -1284,7 +1272,7 @@ begin INT_EXCEL_ID_RK : ReadRKValue(AStream); //(RK) This record represents a cell that contains an RK value (encoded integer or floating-point value). If a floating-point value cannot be encoded to an RK value, a NUMBER record will be written. This record replaces the record INTEGER written in BIFF2. INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); INT_EXCEL_ID_COLINFO : ReadColInfo(AStream); - INT_EXCEL_ID_ROWINFO : ReadRowInfo(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); INT_EXCEL_ID_FORMULA : ReadFormulaExcel(AStream); INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream); diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 680a5efb5..c745bdea1 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -217,23 +217,8 @@ implementation 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_COUNTRY = $008C; - INT_EXCEL_ID_EOF = $000A; - INT_EXCEL_ID_DIMENSIONS = $0200; - INT_EXCEL_ID_FORMULA = $0006; - INT_EXCEL_ID_INDEX = $020B; - INT_EXCEL_ID_ROWINFO = $0208; - INT_EXCEL_ID_STYLE = $0293; - INT_EXCEL_ID_WINDOW2 = $023E; - INT_EXCEL_ID_RSTRING = $00D6; - INT_EXCEL_ID_RK = $027E; - INT_EXCEL_ID_MULRK = $00BD; INT_EXCEL_ID_SST = $00FC; //BIFF8 only - INT_EXCEL_ID_CONTINUE = $003C; INT_EXCEL_ID_LABELSST = $00FD; //BIFF8 only - INT_EXCEL_ID_FORMAT = $041E; INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3; { Cell Addresses constants } @@ -447,6 +432,8 @@ end; * *******************************************************************} procedure TsSpreadBIFF8Writer.WriteToStream(AStream: TStream); +const + isBIFF8 = true; var MyData: TMemoryStream; CurrentPos: Int64; @@ -492,14 +479,17 @@ begin WriteIndex(AStream); WriteColInfos(AStream, sheet); WriteDimensions(AStream, sheet); + //WriteRowAndCellBlock(AStream, sheet); + + WriteRows(AStream, sheet); WriteCellsToStream(AStream, sheet.Cells); + WriteWindow2(AStream, sheet); - WritePane(AStream, sheet, true); // true for "is BIFF5 or BIFF8" + WritePane(AStream, sheet, isBIFF8); WriteEOF(AStream); end; { Cleanup } - SetLength(Boundsheets, 0); end; @@ -1535,7 +1525,7 @@ begin INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); INT_EXCEL_ID_LABELSST: ReadLabelSST(AStream); //BIFF8 only INT_EXCEL_ID_COLINFO : ReadColInfo(AStream); - INT_EXCEL_ID_ROWINFO : ReadRowInfo(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream); INT_EXCEL_ID_BOF : ; diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 5167ecacf..74c074c9b 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -16,21 +16,41 @@ uses const { RECORD IDs which didn't change across versions 2-8 } + INT_EXCEL_ID_EOF = $000A; INT_EXCEL_ID_SELECTION = $001D; - INT_EXCEL_ID_FONT = $0031; + INT_EXCEL_ID_CONTINUE = $003C; INT_EXCEL_ID_PANE = $0041; INT_EXCEL_ID_CODEPAGE = $0042; - INT_EXCEL_ID_COLINFO = $007D; INT_EXCEL_ID_DATEMODE = $0022; - INT_EXCEL_ID_PALETTE = $0092; INT_EXCEL_ID_WINDOW1 = $003D; - INT_EXCEL_ID_XF = $00E0; + + { RECORD IDs which did not change across versions 2, 5, 8} + INT_EXCEL_ID_FORMULA = $0006; // BIFF3: $0206, BIFF4: $0406 + INT_EXCEL_ID_FONT = $0031; // BIFF3-4: $0231 + + { RECORD IDs which did not change across version 3-8} + INT_EXCEL_ID_COLINFO = $007D; // does not exist in BIFF2 + INT_EXCEL_ID_COUNTRY = $008C; // does not exist in BIFF2 + INT_EXCEL_ID_PALETTE = $0092; // does not exist in BIFF2 + INT_EXCEL_ID_DIMENSIONS = $0200; // BIFF2: $0000 + INT_EXCEL_ID_BLANK = $0201; // BIFF2: $0001 + INT_EXCEL_ID_NUMBER = $0203; // BIFF2: $0003 + INT_EXCEL_ID_LABEL = $0204; // BIFF2: $0004 + INT_EXCEL_ID_ROW = $0208; // BIFF2: $0008 + INT_EXCEL_ID_INDEX = $020B; // BIFF2: $000B + INT_EXCEL_ID_WINDOW2 = $023E; // BIFF2: $003E + INT_EXCEL_ID_RK = $027E; // does not exist in BIFF2 + INT_EXCEL_ID_STYLE = $0293; // does not exist in BIFF2 + + { RECORD IDs which did not change across version 4-8 } + INT_EXCEL_ID_FORMAT = $041E; // BIFF2-3: $001E { RECORD IDs which did not change across versions 5-8 } - INT_EXCEL_ID_BLANK = $0201; - INT_EXCEL_ID_NUMBER = $0203; - INT_EXCEL_ID_LABEL = $0204; - INT_EXCEL_ID_WINDOW2 = $023E; + INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs, does not exist before 5 + INT_EXCEL_ID_MULRK = $00BD; // does not exist before BIFF5 + INT_EXCEL_ID_XF = $00E0; // BIFF2:$0043, BIFF3:$0243, BIFF4:$0443 + INT_EXCEL_ID_RSTRING = $00D6; // does not exist before BIFF5 + INT_EXCEL_ID_BOF = $0809; // BIFF2:$0009, BIFF3:$0209; BIFF4:$0409 { FONT record constants } INT_FONT_WEIGHT_NORMAL = $0190; @@ -408,6 +428,12 @@ type // Write out BLANK cell record procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override; + + { + procedure WriteCellBlock(AStream: TStream; ASheet: TsWorksheet; + AFirstRow, ALastRow: Cardinal); + } + // Write out used codepage for character encoding procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding); // Writes out column info(s) @@ -425,6 +451,11 @@ type procedure WritePalette(AStream: TStream); // Writes out a PANE record procedure WritePane(AStream: TStream; ASheet: TsWorksheet; IsBiff58: Boolean); + // Writes out a ROW record + procedure WriteRow(AStream: TStream; ASheet: TsWorksheet; + ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual; + // Write all ROW records for a sheet + procedure WriteRows(AStream: TStream; ASheet: TsWorksheet); // Writes out a SELECTION record procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte); procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet); @@ -924,8 +955,8 @@ begin lRow^.Height := TwipsToMillimeters(h and $7FFF); end else lRow^.Height := 0; - lRow^.AutoHeight := rowrec.Flags and $00000040 = 0; - // If this bit is set row height does not change font height, i.e. has been + //lRow^.AutoHeight := rowrec.Flags and $00000040 = 0; + // If this bit is set row height does not change with font height, i.e. has been // changed manually. end; @@ -1410,6 +1441,88 @@ begin { Not used (BIFF5-BIFF8 only, not written in BIFF2-BIFF4 } end; +{ Writes an Excel 3-8 ROW record + Valid for BIFF3-BIFF8 } +procedure TsSpreadBIFFWriter.WriteRow(AStream: TStream; ASheet: TsWorksheet; + ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); +var + w: Word; + dw: DWord; + cell: PCell; + spaceabove, spacebelow: Boolean; + colindex: Cardinal; + rowheight: Word; +begin + // Check for additional space above/below row + spaceabove := false; + spacebelow := false; + colindex := AFirstColIndex; + while colindex <= ALastColIndex do begin + cell := ASheet.FindCell(ARowindex, colindex); + if (cell <> nil) and (uffBorder in cell^.UsedFormattingFields) then begin + if (cbNorth in cell^.Border) and (cell^.BorderStyles[cbNorth].LineStyle = lsThick) + then spaceabove := true; + if (cbSouth in cell^.Border) and (cell^.BorderStyles[cbSouth].LineStyle = lsThick) + then spacebelow := true; + end; + if spaceabove and spacebelow then break; + inc(colindex); + end; + + { BIFF record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_ROW)); + AStream.WriteWord(WordToLE(16)); + + { Index of row } + AStream.WriteWord(WordToLE(Word(ARowIndex))); + + { Index to column of the first cell which is described by a cell record } + AStream.WriteWord(WordToLE(Word(AFirstColIndex))); + + { Index to column of the last cell which is described by a cell record, increased by 1 } + AStream.WriteWord(WordToLE(Word(ALastColIndex) + 1)); + + { Row height (in twips, 1/20 point) and info on custom row height } + if (ARow = nil) or (ARow^.Height = 0) then + rowheight := round(Workbook.GetFont(0).Size*20) + else + rowheight := MillimetersToTwips(ARow^.Height); + w := rowheight and $7FFF; + AStream.WriteWord(WordToLE(w)); + + { 2 words not used } + AStream.WriteDWord(0); + + { Option flags } + dw := $00000100; // bit 8 is always 1 + if spaceabove then dw := dw or $10000000; + if spacebelow then dw := dw or $20000000; + if (ARow <> nil) then + dw := dw or $00000040; // Row height and font height do not match + AStream.WriteDWord(DWordToLE(dw)); +end; + +{ Writes all ROW records for the given sheet. + Note that the OpenOffice documentation says that rows must be written in + groups of 32, followed by the cells on these rows, etc. THIS IS NOT NECESSARY! + Valid for BIFF2-BIFF8 } +procedure TsSpreadBIFFWriter.WriteRows(AStream: TStream; ASheet: TsWorksheet); +var + row: PRow; + i: Integer; + cell1, cell2: PCell; +begin + for i := 0 to ASheet.Rows.Count-1 do begin + row := ASheet.Rows[i]; + cell1 := ASheet.GetFirstCellOfRow(row^.Row); + if cell1 <> nil then begin + cell2 := ASheet.GetLastCellOfRow(row^.Row); + WriteRow(AStream, ASheet, row^.Row, cell1^.Col, cell2^.Col, row); + end else + WriteRow(AStream, ASheet, row^.Row, 0, 0, row); + end; +end; + { Writes an Excel 2-8 SELECTION record Writes just reasonable default values APane is 0..3 (see below) @@ -1420,19 +1533,19 @@ var activeCellRow, activeCellCol: Word; begin case APane of - 0: begin // right-bottom + 0: begin // right-bottom activeCellRow := ASheet.TopPaneHeight; activeCellCol := ASheet.LeftPaneWidth; end; - 1: begin // right-top + 1: begin // right-top activeCellRow := 0; activeCellCol := ASheet.LeftPaneWidth; end; - 2: begin // left-bottom + 2: begin // left-bottom activeCellRow := ASheet.TopPaneHeight; activeCellCol := 0; end; - 3: begin // left-top + 3: begin // left-top activeCellRow := 0; activeCellCol := 0; end;