From cfb50bf064ef56127e9534536a0e52cb5d18ce63 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Tue, 9 Sep 2014 11:42:20 +0000 Subject: [PATCH] fpspreadsheet: Read default column width and row height from biff and ooxml files git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3532 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpsopendocument.pas | 2 +- components/fpspreadsheet/fpspreadsheet.pas | 39 +----- .../reference/BIFFExplorer/bebiffgrid.pas | 3 +- components/fpspreadsheet/xlsbiff2.pas | 90 ++++++++------ components/fpspreadsheet/xlsbiff5.pas | 106 ++++++++++------- components/fpspreadsheet/xlsbiff8.pas | 35 +++--- components/fpspreadsheet/xlscommon.pas | 111 ++++++++++++------ components/fpspreadsheet/xlsxooxml.pas | 38 +++++- 8 files changed, 245 insertions(+), 179 deletions(-) diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 7bccdb796..f5f937fcd 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -251,7 +251,7 @@ const FALSE_TRUE: Array[boolean] of String = ('false', 'true'); - COLWIDTH_EPS = 1e-2; // for mm + COLWIDTH_EPS = 1e-2; // for mm ROWHEIGHT_EPS = 1e-2; // for lines type diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index a4af7c6a7..1e9ff588b 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1066,11 +1066,13 @@ type FVirtualCell: TCell; {@@ Stores if the reader is in virtual mode } FIsVirtualMode: Boolean; + { Helper methods } {@@ Removes column records if all of them have the same column width } procedure FixCols(AWorksheet: TsWorksheet); {@@ Removes row records if all of them have the same row height } procedure FixRows(AWorksheet: TsWorksheet); + { Record reading methods } {@@ Abstract method for reading a blank cell. Must be overridden by descendent classes. } procedure ReadBlank(AStream: TStream); virtual; abstract; @@ -6583,9 +6585,9 @@ end; {@@ Deletes unnecessary column records as they are written by Office applications - when converting a file to another format. + when they convert a file to another format. - @param AWorksheet The columns in this worksheet are processed. + @param AWorksheet The columns in this worksheet are processed. } procedure TsCustomSpreadReader.FixCols(AWorkSheet: TsWorksheet); const @@ -6593,20 +6595,10 @@ const var c: Cardinal; w: Single; - cLast: Cardinal; begin - if AWorksheet.Cols.Count = 0 then + if AWorksheet.Cols.Count <= 1 then exit; - (* Better to avoid this... - - // Delete all column records after the last column containing an existing cell. - cLast := AWorksheet.GetLastOccupiedColIndex; - for c := AWorksheet.Cols.Count-1 downto 0 do - if PCol(AWorksheet.Cols[c])^.Col > cLast then - AWorksheet.RemoveCol(c); - *) - // Check whether all columns have the same column width w := PCol(AWorksheet.Cols[0])^.Width; for c := 1 to AWorksheet.Cols.Count-1 do @@ -6617,11 +6609,6 @@ begin // to the DefaultColWidth and delete all column records. AWorksheet.DefaultColWidth := w; AWorksheet.RemoveAllCols; - - // To do: - // There's probably more to be done here, such as: - // - if all columns have the same width except for a few use their width as - // DefaultColWidth and delete these records. end; {@@ @@ -6637,18 +6624,9 @@ var rLast: Cardinal; h: Single; begin - if AWorksheet.Rows.Count = 0 then + if AWorksheet.Rows.Count <= 1 then exit; - (* Better to avoid this... - - // Delete all row records after the last row containing an existing cell. - rLast := AWorksheet.GetLastOccupiedRowIndex; - for r := AWorksheet.Rows.Count-1 downto 0 do - if PRow(AWorksheet.Rows[r])^.Row > rLast then - AWorksheet.RemoveRow(r); - *) - // Check whether all rows have the same height h := PRow(AWorksheet.Rows[0])^.Height; for r := 1 to AWorksheet.Rows.Count-1 do @@ -6659,11 +6637,6 @@ begin // to the DefaultRowHeight and delete all row records. AWorksheet.DefaultRowHeight := h; AWorksheet.RemoveAllRows; - - // To do: - // There's probably more to be done here, such as: - // - if all rows have the same height except for a few use their height as - // DefaultRowHeight and delete these records. end; {@@ diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas index 388c04cae..27cf9b9fc 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas +++ b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas @@ -957,7 +957,8 @@ begin numBytes := 2; Move(FBuffer[FBufferIndex], w, numbytes); - ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]), 'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)'); numBytes := 2; diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index 3ea8d526b..0a5bd8a6d 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -66,6 +66,7 @@ type out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); override; procedure ReadBlank(AStream: TStream); override; procedure ReadColWidth(AStream: TStream); + procedure ReadDefRowHeight(AStream: TStream); procedure ReadFont(AStream: TStream); procedure ReadFontColor(AStream: TStream); procedure ReadFormat(AStream: TStream); override; @@ -161,25 +162,26 @@ uses const { Excel record IDs } - INT_EXCEL_ID_DIMENSIONS = $0000; - INT_EXCEL_ID_BLANK = $0001; - INT_EXCEL_ID_INTEGER = $0002; - INT_EXCEL_ID_NUMBER = $0003; - INT_EXCEL_ID_LABEL = $0004; - INT_EXCEL_ID_ROW = $0008; - INT_EXCEL_ID_BOF = $0009; - {%H-}INT_EXCEL_ID_INDEX = $000B; - INT_EXCEL_ID_FORMAT = $001E; - INT_EXCEL_ID_FORMATCOUNT= $001F; - INT_EXCEL_ID_COLWIDTH = $0024; - INT_EXCEL_ID_WINDOW2 = $003E; - INT_EXCEL_ID_XF = $0043; - INT_EXCEL_ID_IXFE = $0044; - INT_EXCEL_ID_FONTCOLOR = $0045; + INT_EXCEL_ID_DIMENSIONS = $0000; + INT_EXCEL_ID_BLANK = $0001; + INT_EXCEL_ID_INTEGER = $0002; + INT_EXCEL_ID_NUMBER = $0003; + INT_EXCEL_ID_LABEL = $0004; + INT_EXCEL_ID_ROW = $0008; + INT_EXCEL_ID_BOF = $0009; + {%H-}INT_EXCEL_ID_INDEX = $000B; + INT_EXCEL_ID_FORMAT = $001E; + INT_EXCEL_ID_FORMATCOUNT = $001F; + INT_EXCEL_ID_COLWIDTH = $0024; + INT_EXCEL_ID_DEFROWHEIGHT = 00025; + INT_EXCEL_ID_WINDOW2 = $003E; + INT_EXCEL_ID_XF = $0043; + INT_EXCEL_ID_IXFE = $0044; + INT_EXCEL_ID_FONTCOLOR = $0045; { BOF record constants } - INT_EXCEL_SHEET = $0010; - {%H-}INT_EXCEL_CHART = $0020; + INT_EXCEL_SHEET = $0010; + {%H-}INT_EXCEL_CHART = $0020; {%H-}INT_EXCEL_MACRO_SHEET = $0040; type @@ -423,9 +425,7 @@ var c, c1, c2: Cardinal; w: Word; col: TCol; - sheet: TsWorksheet; begin - sheet := Workbook.GetFirstWorksheet; // read column start and end index of column range c1 := AStream.ReadByte; c2 := AStream.ReadByte; @@ -433,9 +433,21 @@ begin w := WordLEToN(AStream.ReadWord); // calculate width in units of "characters" col.Width := w / 256; - // assign width to columns - for c := c1 to c2 do - sheet.WriteColInfo(c, col); + // assign width to columns, but only if different from default column width. + if not SameValue(col.Width, FWorksheet.DefaultColWidth) then + for c := c1 to c2 do + FWorksheet.WriteColInfo(c, col); +end; + +procedure TsSpreadBIFF2Reader.ReadDefRowHeight(AStream: TStream); +var + hw: word; + h : Single; +begin + hw := WordLEToN(AStream.ReadWord); + h := TwipsToPts(hw and $8000) / FWorkbook.GetDefaultFontSize; + if h > ROW_HEIGHT_CORRECTION then + FWorksheet.DefaultRowHeight := h - ROW_HEIGHT_CORRECTION; end; procedure TsSpreadBIFF2Reader.ReadFont(AStream: TStream); @@ -509,22 +521,24 @@ begin CurStreamPos := AStream.Position; case RecordType of - INT_EXCEL_ID_BLANK : ReadBlank(AStream); - INT_EXCEL_ID_FONT : ReadFont(AStream); - INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream); - INT_EXCEL_ID_FORMAT : ReadFormat(AStream); - INT_EXCEL_ID_INTEGER : ReadInteger(AStream); - INT_EXCEL_ID_NUMBER : ReadNumber(AStream); - INT_EXCEL_ID_LABEL : ReadLabel(AStream); - INT_EXCEL_ID_FORMULA : ReadFormula(AStream); - INT_EXCEL_ID_STRING : ReadStringRecord(AStream); - INT_EXCEL_ID_COLWIDTH : ReadColWidth(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); - INT_EXCEL_ID_BOF : ; - INT_EXCEL_ID_EOF : BIFF2EOF := True; + INT_EXCEL_ID_BLANK : ReadBlank(AStream); + INT_EXCEL_ID_FONT : ReadFont(AStream); + INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream); + INT_EXCEL_ID_FORMAT : ReadFormat(AStream); + INT_EXCEL_ID_INTEGER : ReadInteger(AStream); + INT_EXCEL_ID_NUMBER : ReadNumber(AStream); + INT_EXCEL_ID_LABEL : ReadLabel(AStream); + INT_EXCEL_ID_FORMULA : ReadFormula(AStream); + INT_EXCEL_ID_STRING : ReadStringRecord(AStream); + INT_EXCEL_ID_COLWIDTH : ReadColWidth(AStream); + INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); + INT_EXCEL_ID_DEFROWHEIGHT: ReadDefRowHeight(AStream); + INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); + INT_EXCEL_ID_PANE : ReadPane(AStream); + INT_EXCEL_ID_XF : ReadXF(AStream); + INT_EXCEL_ID_BOF : ; + INT_EXCEL_ID_EOF : BIFF2EOF := True; else // nothing end; diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index e68179c4a..91a3d9b85 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -82,13 +82,14 @@ type FCurrentWorksheet: Integer; protected { Record writing methods } + procedure ReadBoundsheet(AStream: TStream); procedure ReadFont(const AStream: TStream); procedure ReadFormat(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override; procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook); procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook); - procedure ReadBoundsheet(AStream: TStream); procedure ReadRichString(AStream: TStream); + procedure ReadStandardWidth(AStream: TStream; ASheet: TsWorksheet); procedure ReadStringRecord(AStream: TStream); override; procedure ReadXF(AStream: TStream); public @@ -221,38 +222,39 @@ const // see: in xlscommon { BOF record constants } - INT_BOF_BIFF5_VER = $0500; - INT_BOF_WORKBOOK_GLOBALS= $0005; -{%H-}INT_BOF_VB_MODULE = $0006; - INT_BOF_SHEET = $0010; -{%H-}INT_BOF_CHART = $0020; -{%H-}INT_BOF_MACRO_SHEET = $0040; -{%H-}INT_BOF_WORKSPACE = $0100; - INT_BOF_BUILD_ID = $1FD2; - INT_BOF_BUILD_YEAR = $07CD; + INT_BOF_BIFF5_VER = $0500; + INT_BOF_WORKBOOK_GLOBALS = $0005; +{%H-}INT_BOF_VB_MODULE = $0006; + INT_BOF_SHEET = $0010; +{%H-}INT_BOF_CHART = $0020; +{%H-}INT_BOF_MACRO_SHEET = $0040; +{%H-}INT_BOF_WORKSPACE = $0100; + INT_BOF_BUILD_ID = $1FD2; + INT_BOF_BUILD_YEAR = $07CD; + + { Record IDs } + INT_EXCEL_ID_STANDARDWIDTH = $0099; { FONT record constants } - INT_FONT_WEIGHT_NORMAL = $0190; - -{%H-}BYTE_ANSILatin1 = $00; -{%H-}BYTE_SYSTEM_DEFAULT = $01; -{%H-}BYTE_SYMBOL = $02; -{%H-}BYTE_Apple_Roman = $4D; +{%H-}BYTE_ANSILatin1 = $00; +{%H-}BYTE_SYSTEM_DEFAULT = $01; +{%H-}BYTE_SYMBOL = $02; +{%H-}BYTE_Apple_Roman = $4D; {%H-}BYTE_ANSI_Japanese_Shift_JIS = $80; -{%H-}BYTE_ANSI_Korean_Hangul = $81; -{%H-}BYTE_ANSI_Korean_Johab = $81; +{%H-}BYTE_ANSI_Korean_Hangul = $81; +{%H-}BYTE_ANSI_Korean_Johab = $81; {%H-}BYTE_ANSI_Chinese_Simplified_GBK = $86; {%H-}BYTE_ANSI_Chinese_Traditional_BIG5 = $88; -{%H-}BYTE_ANSI_Greek = $A1; -{%H-}BYTE_ANSI_Turkish = $A2; -{%H-}BYTE_ANSI_Vietnamese = $A3; -{%H-}BYTE_ANSI_Hebrew = $B1; -{%H-}BYTE_ANSI_Arabic = $B2; -{%H-}BYTE_ANSI_Baltic = $BA; -{%H-}BYTE_ANSI_Cyrillic = $CC; -{%H-}BYTE_ANSI_Thai = $DE; -{%H-}BYTE_ANSI_Latin2 = $EE; -{%H-}BYTE_OEM_Latin1 = $FF; +{%H-}BYTE_ANSI_Greek = $A1; +{%H-}BYTE_ANSI_Turkish = $A2; +{%H-}BYTE_ANSI_Vietnamese = $A3; +{%H-}BYTE_ANSI_Hebrew = $B1; +{%H-}BYTE_ANSI_Arabic = $B2; +{%H-}BYTE_ANSI_Baltic = $BA; +{%H-}BYTE_ANSI_Cyrillic = $CC; +{%H-}BYTE_ANSI_Thai = $DE; +{%H-}BYTE_ANSI_Latin2 = $EE; +{%H-}BYTE_OEM_Latin1 = $FF; { FORMULA record constants } {%H-}MASK_FORMULA_RECALCULATE_ALWAYS = $0001; @@ -1160,22 +1162,24 @@ begin case RecordType of - INT_EXCEL_ID_BLANK : ReadBlank(AStream); - INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream); - INT_EXCEL_ID_NUMBER : ReadNumber(AStream); - INT_EXCEL_ID_LABEL : ReadLabel(AStream); - INT_EXCEL_ID_RSTRING : ReadRichString(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). In BIFF8 it is usually replaced by the LABELSST record. Excel still uses this record, if it copies formatted text cells to the clipboard. - 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_ROW : ReadRowInfo(AStream); - INT_EXCEL_ID_FORMULA : ReadFormula(AStream); - INT_EXCEL_ID_SHAREDFMLA: ReadSharedFormula(AStream); - INT_EXCEL_ID_STRING : ReadStringRecord(AStream); - INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); - INT_EXCEL_ID_PANE : ReadPane(AStream); - INT_EXCEL_ID_BOF : ; - INT_EXCEL_ID_EOF : SectionEOF := True; + INT_EXCEL_ID_BLANK : ReadBlank(AStream); + INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream); + INT_EXCEL_ID_NUMBER : ReadNumber(AStream); + INT_EXCEL_ID_LABEL : ReadLabel(AStream); + INT_EXCEL_ID_RSTRING : ReadRichString(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). In BIFF8 it is usually replaced by the LABELSST record. Excel still uses this record, if it copies formatted text cells to the clipboard. + 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_STANDARDWIDTH : ReadStandardWidth(AStream, FWorksheet); + INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); + INT_EXCEL_ID_FORMULA : ReadFormula(AStream); + INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream); + INT_EXCEL_ID_STRING : ReadStringRecord(AStream); + INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); + INT_EXCEL_ID_PANE : ReadPane(AStream); + INT_EXCEL_ID_BOF : ; + INT_EXCEL_ID_EOF : SectionEOF := True; {$IFDEF FPSPREADDEBUG} // Only write out if debugging else @@ -1291,6 +1295,20 @@ begin Workbook.OnReadCellData(Workbook, ARow, ACol, cell); end; +{ Reads the default column width that is used when a bit in the GCW bit structure + is set for the corresponding column. The GCW is ignored here. The column + width read from the STANDARDWIDTH record overrides the one from the + DEFCOLWIDTH record. } +procedure TsSpreadBIFF5Reader.ReadStandardWidth(AStream: TStream; ASheet: TsWorksheet); +var + w: Word; +begin + // read width in 1/256 of the width of "0" character + w := WordLEToN(AStream.ReadWord); + // calculate width in units of "characters" and use it as DefaultColWidth + ASheet.DefaultColWidth := w / 256; +end; + { Reads a STRING record which contains the result of string formula. } procedure TsSpreadBIFF5Reader.ReadStringRecord(AStream: TStream); var diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 7e7a04f21..67529fd5d 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -1430,30 +1430,31 @@ begin case RecordType of - INT_EXCEL_ID_BLANK : ReadBlank(AStream); - INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream); - INT_EXCEL_ID_NUMBER : ReadNumber(AStream); - INT_EXCEL_ID_LABEL : ReadLabel(AStream); - INT_EXCEL_ID_FORMULA : ReadFormula(AStream); - INT_EXCEL_ID_SHAREDFMLA: ReadSharedFormula(AStream); - INT_EXCEL_ID_STRING : ReadStringRecord(AStream); + INT_EXCEL_ID_BLANK : ReadBlank(AStream); + INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream); + INT_EXCEL_ID_NUMBER : ReadNumber(AStream); + INT_EXCEL_ID_LABEL : ReadLabel(AStream); + INT_EXCEL_ID_FORMULA : ReadFormula(AStream); + INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream); + INT_EXCEL_ID_STRING : ReadStringRecord(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). // In BIFF8 it is usually replaced by the LABELSST record. Excel still // uses this record, if it copies formatted text cells to the clipboard. - INT_EXCEL_ID_RSTRING : ReadRichString(AStream); + INT_EXCEL_ID_RSTRING : ReadRichString(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_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_ROW : ReadRowInfo(AStream); - INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); - INT_EXCEL_ID_PANE : ReadPane(AStream); - INT_EXCEL_ID_BOF : ; - INT_EXCEL_ID_EOF : SectionEOF := True; + INT_EXCEL_ID_RK : ReadRKValue(AStream); + INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); + INT_EXCEL_ID_LABELSST : ReadLabelSST(AStream); //BIFF8 only + INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream); + INT_EXCEL_ID_COLINFO : ReadColInfo(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); + INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); + INT_EXCEL_ID_PANE : ReadPane(AStream); + INT_EXCEL_ID_BOF : ; + INT_EXCEL_ID_EOF : SectionEOF := True; else // nothing end; diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index b80dbcf63..ff9c4c1c6 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -22,50 +22,52 @@ 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_CONTINUE = $003C; - INT_EXCEL_ID_PANE = $0041; - INT_EXCEL_ID_CODEPAGE = $0042; - INT_EXCEL_ID_DATEMODE = $0022; - INT_EXCEL_ID_WINDOW1 = $003D; + INT_EXCEL_ID_EOF = $000A; + INT_EXCEL_ID_SELECTION = $001D; + INT_EXCEL_ID_CONTINUE = $003C; + INT_EXCEL_ID_DATEMODE = $0022; + INT_EXCEL_ID_WINDOW1 = $003D; + INT_EXCEL_ID_PANE = $0041; + INT_EXCEL_ID_CODEPAGE = $0042; + INT_EXCEL_ID_DEFCOLWIDTH = $0055; { 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 + 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_SHEETPR = $0081; // 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_STRING = $0207; // BIFF2: $0007 - 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 + INT_EXCEL_ID_COLINFO = $007D; // does not exist in BIFF2 + INT_EXCEL_ID_SHEETPR = $0081; // 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_STRING = $0207; // BIFF2: $0007 + INT_EXCEL_ID_ROW = $0208; // BIFF2: $0008 + INT_EXCEL_ID_INDEX = $020B; // BIFF2: $000B + INT_EXCEL_ID_DEFROWHEIGHT= $0225; // BIFF2: $0025 + 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_PAGESETUP = $00A1; // does not exist before BIFF4 - INT_EXCEL_ID_FORMAT = $041E; // BIFF2-3: $001E + INT_EXCEL_ID_PAGESETUP = $00A1; // does not exist before BIFF4 + INT_EXCEL_ID_FORMAT = $041E; // BIFF2-3: $001E { RECORD IDs which did not change across versions 5-8 } - 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_MULBLANK = $00BE; // 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_SHAREDFMLA = $04BC; // does not exist before BIFF5 - INT_EXCEL_ID_BOF = $0809; // BIFF2:$0009, BIFF3:$0209; BIFF4:$0409 + 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_MULBLANK = $00BE; // 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_SHAREDFMLA = $04BC; // does not exist before BIFF5 + INT_EXCEL_ID_BOF = $0809; // BIFF2:$0009, BIFF3:$0209; BIFF4:$0409 { FONT record constants } - INT_FONT_WEIGHT_NORMAL = $0190; - INT_FONT_WEIGHT_BOLD = $02BC; + INT_FONT_WEIGHT_NORMAL = $0190; + INT_FONT_WEIGHT_BOLD = $02BC; { CODEPAGE record constants } WORD_ASCII = 367; @@ -82,8 +84,8 @@ const WORD_CP_1258_Latin1_BIFF2_3 = 32769; // BIFF2-BIFF3 { DATEMODE record, 5.28 } - DATEMODE_1900_BASE=1; //1/1/1900 minus 1 day in FPC TDateTime - DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime + DATEMODE_1900_BASE = 1; //1/1/1900 minus 1 day in FPC TDateTime + DATEMODE_1904_BASE = 1462; //1/1/1904 in FPC TDateTime { WINDOW1 record constants - BIFF5-BIFF8 } MASK_WINDOW1_OPTION_WINDOW_HIDDEN = $0001; @@ -245,6 +247,10 @@ type procedure ReadColInfo(const AStream: TStream); // Figures out what the base year for dates is for this file procedure ReadDateMode(AStream: TStream); + // Reads the default column width + procedure ReadDefColWidth(AStream: TStream); + // Reas the default row height + procedure ReadDefRowHeight(AStream: TStream); // Read FORMAT record (cell formatting) procedure ReadFormat(AStream: TStream); virtual; // Read FORMULA record @@ -963,6 +969,8 @@ end; Valid for BIFF3-BIFF8. For BIFF2 use the records COLWIDTH and COLUMNDEFAULT. } procedure TsSpreadBiffReader.ReadColInfo(const AStream: TStream); +const + EPS = 1E-2; // allow for large epsilon because col width calculation is not very well-defined... var c, c1, c2: Cardinal; w: Word; @@ -975,9 +983,10 @@ begin 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); + // assign width to columns, but only if different from default column width + if not SameValue(col.Width, FWorksheet.DefaultColWidth, EPS) then + for c := c1 to c2 do + FWorksheet.WriteColInfo(c, col); end; procedure TsSpreadBIFFReader.ReadDateMode(AStream: TStream); @@ -1002,6 +1011,30 @@ begin end; end; +// Reads the default column width +procedure TsSpreadBIFFReader.ReadDefColWidth(AStream: TStream); +begin + // The file contains the column width in characters + FWorksheet.DefaultColWidth := WordLEToN(AStream.ReadWord); +end; + +// Reads the default row height +// Valid for BIFF3 - BIFF8 (override for BIFF2) +procedure TsSpreadBIFFReader.ReadDefRowHeight(AStream: TStream); +var + options, hw: Word; + h: Single; +begin + // Options + AStream.ReadWord; + + // Height, in Twips (1/20 pt). + hw := WordLEToN(AStream.ReadWord); + h := TwipsToPts(hw) / FWorkbook.GetDefaultFontSize; + if h > ROW_HEIGHT_CORRECTION then + FWorksheet.DefaultRowHeight := h - ROW_HEIGHT_CORRECTION; +end; + // Read the FORMAT record for formatting numerical data procedure TsSpreadBIFFReader.ReadFormat(AStream: TStream); begin diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas index a1916ab68..f5ed00e56 100755 --- a/components/fpspreadsheet/xlsxooxml.pas +++ b/components/fpspreadsheet/xlsxooxml.pas @@ -80,6 +80,7 @@ type procedure ReadPalette(ANode: TDOMNode); procedure ReadRowHeight(ANode: TDOMNode; AWorksheet: TsWorksheet); procedure ReadSharedStrings(ANode: TDOMNode); + procedure ReadSheetFormatPr(ANode: TDOMNode; AWorksheet: TsWorksheet); procedure ReadSheetList(ANode: TDOMNode; AList: TStrings); procedure ReadSheetViews(ANode: TDOMNode; AWorksheet: TsWorksheet); procedure ReadThemeElements(ANode: TDOMNode); @@ -866,6 +867,8 @@ begin end; procedure TsSpreadOOXMLReader.ReadCols(ANode: TDOMNode; AWorksheet: TsWorksheet); +const + EPS = 1e-2; var colNode: TDOMNode; col, col1, col2: Cardinal; @@ -884,11 +887,10 @@ begin s := GetAttrValue(colNode, 'max'); if s <> '' then col2 := StrToInt(s)-1 else col2 := col1; s := GetAttrValue(colNode, 'width'); - if s <> '' then begin - w := StrToFloat(s, FPointSeparatorSettings); - for col := col1 to col2 do - AWorksheet.WriteColWidth(col, w); - end; + if (s <> '') and TryStrToFloat(s, w, FPointSeparatorSettings) then + if not SameValue(w, AWorksheet.DefaultColWidth, EPS) then + for col := col1 to col2 do + AWorksheet.WriteColWidth(col, w); end; colNode := colNode.NextSibling; end; @@ -1189,6 +1191,29 @@ begin end; end; +procedure TsSpreadOOXMLReader.ReadSheetFormatPr(ANode: TDOMNode; + AWorksheet: TsWorksheet); +var + w, h: Single; + s: String; +begin + if ANode = nil then + exit; + + s := GetAttrValue(ANode, 'defaultColWidth'); // is in characters + if (s <> '') and TryStrToFloat(s, w, FPointSeparatorSettings) then + AWorksheet.DefaultColWidth := w; + + s := GetAttrValue(ANode, 'defaultRowHeight'); // in in points + if (s <> '') and TryStrToFloat(s, h, FPointSeparatorSettings) then begin + h := h / Workbook.GetDefaultFontSize; + if h > ROW_HEIGHT_CORRECTION then begin + h := h - ROW_HEIGHT_CORRECTION; + AWorksheet.DefaultRowHeight := h; + end; + end; +end; + procedure TsSpreadOOXMLReader.ReadSheetList(ANode: TDOMNode; AList: TStrings); var node: TDOMNode; @@ -1340,8 +1365,8 @@ begin end; rownode := rownode.NextSibling; end; - FixRows(AWorksheet); FixCols(AWorksheet); + FixRows(AWorksheet); end; procedure TsSpreadOOXMLReader.ReadFromFile(AFileName: string; AData: TsWorkbook); @@ -1440,6 +1465,7 @@ begin FWorksheet := AData.AddWorksheet(SheetList[i]); ReadSheetViews(Doc.DocumentElement.FindNode('sheetViews'), FWorksheet); + ReadSheetFormatPr(Doc.DocumentElement.FindNode('sheetFormatPr'), FWorksheet); ReadCols(Doc.DocumentElement.FindNode('cols'), FWorksheet); ReadWorksheet(Doc.DocumentElement.FindNode('sheetData'), FWorksheet);