From 4ce25109a8af7e8f42eac4d6cc2dfd10bfaf6dc0 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 1 Mar 2017 20:01:14 +0000 Subject: [PATCH] fpspreadsheet: Read/write worksheet hidden state for biff8 and biff5. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5775 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../fpspreadsheet/source/common/xlsbiff5.pas | 55 +++++++----- .../fpspreadsheet/source/common/xlsbiff8.pas | 60 ++++++++----- .../fpspreadsheet/source/common/xlscommon.pas | 90 ++++++++++--------- 3 files changed, 116 insertions(+), 89 deletions(-) diff --git a/components/fpspreadsheet/source/common/xlsbiff5.pas b/components/fpspreadsheet/source/common/xlsbiff5.pas index 650a9ccc1..006b71ce0 100644 --- a/components/fpspreadsheet/source/common/xlsbiff5.pas +++ b/components/fpspreadsheet/source/common/xlsbiff5.pas @@ -100,7 +100,7 @@ type procedure InternalWriteToStream(AStream: TStream); { Record writing methods } procedure WriteBOF(AStream: TStream; ADataType: Word); - function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; + function WriteBoundsheet(AStream: TStream; AWorkSheet: TsWorksheet): Int64; procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet; const AName: String; AIndexToREF: Word); override; procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); @@ -364,9 +364,10 @@ end; -------------------------------------------------------------------------------} procedure TsSpreadBIFF5Reader.ReadBoundsheet(AStream: TStream); var - Len: Byte; + len: Byte; s: AnsiString; - sheetName: String; + sheetState: Byte; + sheetData: TsSheetData; begin { Absolute stream position of the BOF record of the sheet represented by this record } @@ -374,18 +375,21 @@ begin AStream.ReadDWord(); { Visibility } - AStream.ReadByte(); + sheetState := AStream.ReadByte(); { Sheet type } AStream.ReadByte(); { Sheet name: Byte string, 8-bit length } - Len := AStream.ReadByte(); + len := AStream.ReadByte(); + SetLength(s, len); + AStream.ReadBuffer(s[1], len*SizeOf(AnsiChar)); - SetLength(s, Len); - AStream.ReadBuffer(s[1], Len*SizeOf(AnsiChar)); - sheetName := ConvertEncoding(s, FCodePage, EncodingUTF8); - FWorksheetNames.Add(sheetName); + { Temporarily store parameters for worksheet in FSheetList } + sheetData := TsSheetData.Create; + sheetData.Name := ConvertEncoding(s, FCodePage, EncodingUTF8); + sheetData.Hidden := sheetState <> 0; + FSheetList.Add(sheetData); end; {@@ ---------------------------------------------------------------------------- @@ -494,8 +498,12 @@ var SectionEOF: Boolean = False; RecordType: Word; CurStreamPos: Int64; + sheetData: TsSheetData; begin - FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurSheetIndex], true); + sheetData := TsSheetData(FSheetList[FCurSheetIndex]); + FWorksheet := FWorkbook.AddWorksheet(sheetData.Name, true); + if sheetData.Hidden then + FWorksheet.Options := FWorksheet.Options + [soHidden]; while (not SectionEOF) do begin @@ -1101,7 +1109,7 @@ end; procedure TsSpreadBIFF5Writer.InternalWriteToStream(AStream: TStream); var CurrentPos: Int64; - Boundsheets: array of Int64; + sheetPos: array of Int64; i: Integer; pane: Byte; begin @@ -1121,9 +1129,9 @@ begin WriteStyle(AStream); // A BOUNDSHEET for each worksheet - SetLength(Boundsheets, Workbook.GetWorksheetCount); + SetLength(sheetPos, Workbook.GetWorksheetCount); for i := 0 to Workbook.GetWorksheetCount - 1 do - Boundsheets[i] := WriteBoundsheet(AStream, Workbook.GetWorksheetByIndex(i).Name); + sheetPos[i] := WriteBoundsheet(AStream, Workbook.GetWorksheetByIndex(i)); WriteEOF(AStream); @@ -1136,7 +1144,7 @@ begin { First goes back and writes the position of the BOF of the sheet on the respective BOUNDSHEET record } CurrentPos := AStream.Position; - AStream.Position := Boundsheets[i]; + AStream.Position := sheetPos[i]; AStream.WriteDWord(CurrentPos); AStream.Position := CurrentPos; @@ -1179,7 +1187,7 @@ begin { Cleanup } - SetLength(Boundsheets, 0); + SetLength(sheetPos, 0); end; {@@ ---------------------------------------------------------------------------- @@ -1281,13 +1289,15 @@ end; @return The stream position where the absolute stream position of the BOF of this sheet should be written (4 bytes size). -------------------------------------------------------------------------------} -function TsSpreadBIFF5Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; +function TsSpreadBIFF5Writer.WriteBoundsheet(AStream: TStream; + AWorkSheet: TsWorksheet): Int64; var - Len: Byte; + len: Byte; xlsSheetName: ansistring; + sheetState: Byte; begin - xlsSheetName := ConvertEncoding(ASheetName, encodingUTF8, FCodePage); - Len := Length(xlsSheetName); + xlsSheetName := ConvertEncoding(AWorkSheet.Name, encodingUTF8, FCodePage); + len := Length(xlsSheetName); { LatinSheetName := UTF8ToISO_8859_1(ASheetName); Len := Length(LatinSheetName); @@ -1301,15 +1311,16 @@ begin AStream.WriteDWord(WordToLE(0)); { Visibility } - AStream.WriteByte(0); + sheetState := IfThen(soHidden in AWorksheet.Options, 1, 0); + AStream.WriteByte(sheetState); { Sheet type } AStream.WriteByte(0); { Sheet name: Byte string, 8-bit length } - AStream.WriteByte(Len); + AStream.WriteByte(len); // AStream.WriteBuffer(LatinSheetName[1], Len); - AStream.WriteBuffer(xlsSheetName[1], Len); + AStream.WriteBuffer(xlsSheetName[1], len); end; {@@ ---------------------------------------------------------------------------- diff --git a/components/fpspreadsheet/source/common/xlsbiff8.pas b/components/fpspreadsheet/source/common/xlsbiff8.pas index 9151f86b8..f03047388 100644 --- a/components/fpspreadsheet/source/common/xlsbiff8.pas +++ b/components/fpspreadsheet/source/common/xlsbiff8.pas @@ -65,7 +65,6 @@ uses fpsutils; type - TBIFF8ExternSheet = packed record ExternBookIndex: Word; FirstSheetIndex: Word; @@ -139,7 +138,7 @@ type { Record writing methods } procedure WriteBOF(AStream: TStream; ADataType: Word); - function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; + function WriteBoundsheet(AStream: TStream; AWorksheet: TsWorksheet): Int64; procedure WriteComment(AStream: TStream; ACell: PCell); override; procedure WriteComments(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet; @@ -454,6 +453,7 @@ type Text: String; end; + { TsSpreadBIFF8Reader } destructor TsSpreadBIFF8Reader.Destroy; @@ -830,8 +830,12 @@ var SectionEOF: Boolean = False; RecordType: Word; CurStreamPos: Int64; + sheetData: TsSheetData; begin - FWorksheet := FWorkbook.AddWorksheet(FWorksheetNames[FCurSheetIndex], true); + sheetData := TsSheetData(FSheetList[FCurSheetIndex]); + FWorksheet := FWorkbook.AddWorksheet(sheetData.Name, true); + if sheetData.Hidden then + FWorksheet.Options := FWorksheet.Options + [soHidden]; while (not SectionEOF) do begin @@ -912,9 +916,11 @@ end; procedure TsSpreadBIFF8Reader.ReadBoundsheet(AStream: TStream); var - Len: Byte; - WideName: WideString; + len: Byte; + wideName: WideString; rtParams: TsRichTextParams; + sheetstate: Byte; + sheetdata: TsSheetData; begin { Absolute stream position of the BOF record of the sheet represented by this record } @@ -922,18 +928,21 @@ begin AStream.ReadDWord(); { Visibility } - AStream.ReadByte(); + sheetstate := AStream.ReadByte(); // 0=visible, 1=hidden, 2="very" hidden { Sheet type } AStream.ReadByte(); { Sheet name: 8-bit length } - Len := AStream.ReadByte(); + len := AStream.ReadByte(); { Read string with flags } - WideName:=ReadWideString(AStream, Len, rtParams); + wideName := ReadWideString(AStream, len, rtParams); - FWorksheetNames.Add(UTF8Encode(WideName)); + sheetData := TsSheetData.Create; + sheetData.Name := UTF8Encode(wideName); + sheetData.Hidden := sheetState <> 0; + FSheetList.Add(sheetdata); end; function TsSpreadBIFF8Reader.ReadString(const AStream: TStream; @@ -2093,8 +2102,8 @@ procedure TsSpreadBIFF8Writer.InternalWriteToStream(AStream: TStream); const isBIFF8 = true; var - CurrentPos: Int64; - Boundsheets: array of Int64; + currentPos: Int64; + sheetPos: array of Int64; i: Integer; pane: Byte; begin @@ -2109,9 +2118,9 @@ begin WriteStyle(AStream); // A BOUNDSHEET for each worksheet - SetLength(Boundsheets, Workbook.GetWorksheetCount); + SetLength(sheetPos, Workbook.GetWorksheetCount); for i := 0 to Workbook.GetWorksheetCount - 1 do - Boundsheets[i] := WriteBoundsheet(AStream, Workbook.GetWorksheetByIndex(i).Name); + sheetPos[i] := WriteBoundsheet(AStream, Workbook.GetWorksheetByIndex(i)); WriteEXTERNBOOK(AStream); WriteEXTERNSHEET(AStream); @@ -2126,10 +2135,10 @@ begin { First goes back and writes the position of the BOF of the sheet on the respective BOUNDSHEET record } - CurrentPos := AStream.Position; - AStream.Position := Boundsheets[i]; + currentPos := AStream.Position; + AStream.Position := sheetPos[i]; AStream.WriteDWord(DWordToLE(DWORD(CurrentPos))); - AStream.Position := CurrentPos; + AStream.Position := currentPos; WriteBOF(AStream, INT_BOF_SHEET); WriteIndex(AStream); @@ -2175,7 +2184,7 @@ begin end; { Cleanup } - SetLength(Boundsheets, 0); + SetLength(sheetPos, 0); end; {@@ ---------------------------------------------------------------------------- @@ -2285,13 +2294,15 @@ end; @return The stream position where the absolute stream position of the BOF of this sheet should be written (4 bytes size). -------------------------------------------------------------------------------} -function TsSpreadBIFF8Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64; +function TsSpreadBIFF8Writer.WriteBoundsheet(AStream: TStream; + AWorksheet: TsWorksheet): Int64; var - Len: Byte; - WideSheetName: WideString; + len: Byte; + wideSheetName: WideString; + sheetState: Byte; begin - WideSheetName:=UTF8Decode(ASheetName); - Len := Length(WideSheetName); + wideSheetName := UTF8Decode(AWorksheet.Name); + len := Length(wideSheetName); { BIFF Record header } WriteBIFFHeader(AStream, INT_EXCEL_ID_BOUNDSHEET, 8 + Len * Sizeof(WideChar)); @@ -2302,7 +2313,8 @@ begin AStream.WriteDWord(DWordToLE(0)); { Visibility } - AStream.WriteByte(0); + sheetState := IfThen(soHidden in AWorksheet.Options, 1, 0); + AStream.WriteByte(sheetState); { Sheet type } AStream.WriteByte(0); @@ -2311,7 +2323,7 @@ begin AStream.WriteByte(Len); {String flags} AStream.WriteByte(1); - AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar)); + AStream.WriteBuffer(WideStringToLE(wideSheetName)[1], len * SizeOf(WideChar)); end; {@@ ---------------------------------------------------------------------------- diff --git a/components/fpspreadsheet/source/common/xlscommon.pas b/components/fpspreadsheet/source/common/xlscommon.pas index c47e74264..aacfbf3ed 100644 --- a/components/fpspreadsheet/source/common/xlscommon.pas +++ b/components/fpspreadsheet/source/common/xlscommon.pas @@ -346,6 +346,12 @@ type function ConvertToExcelError(AValue: TsErrorValue): byte; type + { TsSheetData } + TsSheetData = class + Name: String; + Hidden: Boolean; + end; + { TsBIFFHeader } TsBIFFHeader = packed record RecordID: Word; @@ -379,10 +385,11 @@ type FFirstNumFormatIndexInFile: Integer; FPalette: TsPalette; FDefinedNames: TFPList; - FWorksheetNames: TStrings; + FWorksheetData: TFPList; FCurSheetIndex: Integer; FActivePane: Integer; FExternSheets: TStrings; + FSheetList: TFPList; procedure AddBuiltinNumFormats; override; procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual; @@ -712,7 +719,6 @@ type TextLen: Word; end; - function ConvertExcelDateTimeToDateTime(const AExcelDateNum: Double; ADateMode: TDateMode): TDateTime; begin @@ -936,6 +942,8 @@ constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook); begin inherited Create(AWorkbook); + FSheetList := TFPList.Create; + FPalette := TsPalette.Create; PopulatePalette; @@ -967,6 +975,9 @@ begin for j:=0 to FDefinedNames.Count-1 do TObject(FDefinedNames[j]).Free; FDefinedNames.Free; + for j:= 0 to FSheetList.Count-1 do TObject(FSheetList[j]).Free; + FSheetList.Free; + FExternSheets.Free; FPalette.Free; @@ -2885,55 +2896,48 @@ var i: Integer; sheet: TsWorksheet; begin - // Check if the operation succeeded - if AStream.Size = 0 then - raise Exception.Create('[TsSpreadBIFFReader.InternalReadFromStream] Reading of OLE document failed'); + // Check if the operation succeeded + if AStream.Size = 0 then + raise Exception.Create('[TsSpreadBIFFReader.InternalReadFromStream] Reading of OLE document failed'); - // Rewind the stream and read from it - AStream.Position := 0; + // Rewind the stream and read from it + AStream.Position := 0; - {Initializations } - FWorksheetNames := TStringList.Create; - try - FCurSheetIndex := 0; - BIFFEOF := false; + {Initializations } + FCurSheetIndex := 0; + BIFFEOF := false; - { Read workbook globals } - ReadWorkbookGlobals(AStream); + { Read workbook globals } + ReadWorkbookGlobals(AStream); - { Check for the end of the file } - if AStream.Position >= AStream.Size then - BIFFEOF := true; + { Check for the end of the file } + if AStream.Position >= AStream.Size then + BIFFEOF := true; - { Now read all worksheets } - while not BIFFEOF do - begin - ReadWorksheet(AStream); + { Now read all worksheets } + while not BIFFEOF do + begin + ReadWorksheet(AStream); - // Check for the end of the file - if AStream.Position >= AStream.Size then - BIFFEOF := true; + // Check for the end of the file + if AStream.Position >= AStream.Size then + BIFFEOF := true; - // Final preparations - inc(FCurSheetIndex); - // It can happen in files written by Office97 that the OLE directory is - // at the end of the file. - if FCurSheetIndex = FWorksheetNames.Count then - BIFFEOF := true; - end; + // Final preparations + inc(FCurSheetIndex); + // It can happen in files written by Office97 that the OLE directory is + // at the end of the file. + if FCurSheetIndex = FSheetList.Count then + BIFFEOF := true; + end; - { Extract print ranges, repeated rows/cols } - for i:=0 to FWorkbook.GetWorksheetCount-1 do begin - sheet := FWorkbook.GetWorksheetByIndex(i); - FixDefinedNames(sheet); - ExtractPrintRanges(sheet); - ExtractPrintTitles(sheet); - end; - - finally - { Finalization } - FreeAndNil(FWorksheetNames); - end; + { Extract print ranges, repeated rows/cols } + for i:=0 to FWorkbook.GetWorksheetCount-1 do begin + sheet := FWorkbook.GetWorksheetByIndex(i); + FixDefinedNames(sheet); + ExtractPrintRanges(sheet); + ExtractPrintTitles(sheet); + end; end;