fpspreadsheet: Add reading of cell/sheet/workbook protection for xls biff8.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5789 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2017-03-05 16:52:17 +00:00
parent 6126788bec
commit 2f54a9bfca
2 changed files with 174 additions and 87 deletions

View File

@ -368,6 +368,9 @@ const
{ XF CELL BACKGROUND PATTERN }
MASK_XF_BACKGROUND_PATTERN = $FC000000;
{ XP CELL PROTECTION }
MASK_XF_PROTECTION = $0007;
{ HLINK FLAGS }
MASK_HLINK_LINK = $00000001;
MASK_HLINK_ABSOLUTE = $00000002;
@ -797,19 +800,22 @@ begin
if RecordType <> INT_EXCEL_ID_CONTINUE then begin
case RecordType of
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_DEFINEDNAME : ReadDEFINEDNAME(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream);
INT_EXCEL_ID_SST : ReadSST(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodepage(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
INT_EXCEL_ID_XF : ReadXF(AStream);
INT_EXCEL_ID_DATEMODE : ReadDateMode(AStream);
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
else
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodepage(AStream);
INT_EXCEL_ID_DATEMODE : ReadDateMode(AStream);
INT_EXCEL_ID_DEFINEDNAME : ReadDEFINEDNAME(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream);
INT_EXCEL_ID_PROTECT : ReadPROTECT(AStream);
INT_EXCEL_ID_SST : ReadSST(AStream);
INT_EXCEL_ID_WINDOWPROTECT : ReadWindowProtect(AStream);
INT_EXCEL_ID_XF : ReadXF(AStream);
else
// nothing
end;
end;
@ -874,6 +880,8 @@ begin
INT_EXCEL_ID_OBJ : ReadOBJ(AStream);
INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet);
INT_EXCEL_ID_PROTECT : ReadPROTECT(AStream, FWorksheet);
INT_EXCEL_ID_PRINTGRID : ReadPrintGridLines(AStream);
INT_EXCEL_ID_PRINTHEADERS : ReadPrintHeaders(AStream);
INT_EXCEL_ID_RIGHTMARGIN : ReadMargin(AStream, 1);
@ -1678,6 +1686,14 @@ begin
end;
end;
// Protection
case WordLEToN(rec.XFType_Prot_ParentXF) and MASK_XF_PROTECTION of
0: fmt.Protection := [];
1: fmt.Protection := [cpLockCell];
2: fmt.Protection := [cpHideFormulas];
3: fmt.Protection := [cpLockCell, cpHideFormulas];
end;
// Add the XF to the internal cell format list
FCellFormatList.Add(fmt);
end;
@ -1776,7 +1792,7 @@ begin
{ Height of the font in twips = 1/20 of a point }
lHeight := WordLEToN(AStream.ReadWord);
font.Size := lHeight/20;
font.Size := lHeight / 20;
{ Option flags }
lOptions := WordLEToN(AStream.ReadWord);

View File

@ -16,94 +16,97 @@ uses
const
{ RECORD IDs which didn't change across versions 2-8 }
INT_EXCEL_ID_EOF = $000A;
INT_EXCEL_ID_HEADER = $0014;
INT_EXCEL_ID_FOOTER = $0015;
INT_EXCEL_ID_EXTERNSHEET = $0017;
INT_EXCEL_ID_NOTE = $001C;
INT_EXCEL_ID_SELECTION = $001D;
INT_EXCEL_ID_DATEMODE = $0022;
INT_EXCEL_ID_LEFTMARGIN = $0026;
INT_EXCEL_ID_RIGHTMARGIN = $0027;
INT_EXCEL_ID_TOPMARGIN = $0028;
INT_EXCEL_ID_BOTTOMMARGIN= $0029;
INT_EXCEL_ID_PRINTHEADERS= $002A;
INT_EXCEL_ID_PRINTGRID = $002B;
INT_EXCEL_ID_CONTINUE = $003C;
INT_EXCEL_ID_WINDOW1 = $003D;
INT_EXCEL_ID_PANE = $0041;
INT_EXCEL_ID_CODEPAGE = $0042;
INT_EXCEL_ID_DEFCOLWIDTH = $0055;
INT_EXCEL_ID_EOF = $000A;
INT_EXCEL_ID_PROTECT = $0012;
INT_EXCEL_ID_PASSWORD = $0013;
INT_EXCEL_ID_HEADER = $0014;
INT_EXCEL_ID_FOOTER = $0015;
INT_EXCEL_ID_EXTERNSHEET = $0017;
INT_EXCEL_ID_WINDOWPROTECT = $0019;
INT_EXCEL_ID_NOTE = $001C;
INT_EXCEL_ID_SELECTION = $001D;
INT_EXCEL_ID_DATEMODE = $0022;
INT_EXCEL_ID_LEFTMARGIN = $0026;
INT_EXCEL_ID_RIGHTMARGIN = $0027;
INT_EXCEL_ID_TOPMARGIN = $0028;
INT_EXCEL_ID_BOTTOMMARGIN = $0029;
INT_EXCEL_ID_PRINTHEADERS = $002A;
INT_EXCEL_ID_PRINTGRID = $002B;
INT_EXCEL_ID_CONTINUE = $003C;
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 changed across versions 2-5 }
INT_EXCEL_ID_EXTERNCOUNT = $0016; // does not exist in BIFF8
INT_EXCEL_ID_EXTERNCOUNT = $0016; // does not exist in BIFF8
{ RECORD IDs which did not change across versions 2, 5, 8}
INT_EXCEL_ID_FORMULA = $0006; // BIFF3: $0206, BIFF4: $0406
INT_EXCEL_ID_DEFINEDNAME = $0018; // BIFF3-4: $0218
INT_EXCEL_ID_FONT = $0031; // BIFF3-4: $0231
INT_EXCEL_ID_FORMULA = $0006; // BIFF3: $0206, BIFF4: $0406
INT_EXCEL_ID_DEFINEDNAME = $0018; // BIFF3-4: $0218
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_HCENTER = $0083; // does not exist in BIFF2
INT_EXCEL_ID_VCENTER = $0084; // 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_BOOLERROR = $0205; // BIFF2: $0005
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
INT_EXCEL_ID_COLINFO = $007D; // does not exist in BIFF2
INT_EXCEL_ID_SHEETPR = $0081; // does not exist in BIFF2
INT_EXCEL_ID_HCENTER = $0083; // does not exist in BIFF2
INT_EXCEL_ID_VCENTER = $0084; // 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_BOOLERROR = $0205; // BIFF2: $0005
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_SCL = $00A0; // does not exist before BIFF4
INT_EXCEL_ID_PAGESETUP = $00A1; // does not exist before BIFF4
INT_EXCEL_ID_FORMAT = $041E; // BIFF2-3: $001E
INT_EXCEL_ID_SCL = $00A0; // does not exist before BIFF4
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_OBJ = $005D; // does not exist before BIFF5
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_OBJ = $005D; // does not exist before BIFF5
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;
WORD_CP_437_DOS_US = 437;
WORD_CP_850_DOS_Latin1 = 850;
WORD_CP_852_DOS_Latin2 = 852;
WORD_CP_866_DOS_Cyrillic = 866;
WORD_CP_874_Thai = 874;
WORD_UTF_16 = 1200; // BIFF 8
WORD_CP_1250_Latin2 = 1250;
WORD_CP_1251_Cyrillic = 1251;
WORD_CP_1252_Latin1 = 1252; // BIFF4-BIFF5
WORD_CP_1253_Greek = 1253;
WORD_CP_1254_Turkish = 1254;
WORD_CP_1255_Hebrew = 1255;
WORD_CP_1256_Arabic = 1256;
WORD_CP_1257_Baltic = 1257;
WORD_CP_1258_Vietnamese = 1258;
WORD_CP_1258_Latin1_BIFF2_3 = 32769; // BIFF2-BIFF3
WORD_ASCII = 367;
WORD_CP_437_DOS_US = 437;
WORD_CP_850_DOS_Latin1 = 850;
WORD_CP_852_DOS_Latin2 = 852;
WORD_CP_866_DOS_Cyrillic = 866;
WORD_CP_874_Thai = 874;
WORD_UTF_16 = 1200; // BIFF 8
WORD_CP_1250_Latin2 = 1250;
WORD_CP_1251_Cyrillic = 1251;
WORD_CP_1252_Latin1 = 1252; // BIFF4-BIFF5
WORD_CP_1253_Greek = 1253;
WORD_CP_1254_Turkish = 1254;
WORD_CP_1255_Hebrew = 1255;
WORD_CP_1256_Arabic = 1256;
WORD_CP_1257_Baltic = 1257;
WORD_CP_1258_Vietnamese = 1258;
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;
@ -328,7 +331,7 @@ const
ROWHEIGHT_EPS = 1E-2;
type
TDateMode=(dm1900, dm1904); //DATEMODE values, 5.28
TDateMode = (dm1900, dm1904); //DATEMODE values, 5.28
// Adjusts Excel float (date, date/time, time) with the file's base date to get a TDateTime
function ConvertExcelDateTimeToDateTime
@ -451,8 +454,12 @@ type
procedure ReadPageSetup(AStream: TStream);
// Read PANE record
procedure ReadPane(AStream: TStream);
// Read PASSWORD record
procedure ReadPASSWORD(AStream: TStream; AWorksheet: TsWorksheet = nil);
procedure ReadPrintGridLines(AStream: TStream);
procedure ReadPrintHeaders(AStream: TStream);
// Reads whether the workbook or worksheet is protected
procedure ReadPROTECT(AStream: TStream; AWorksheet: TsWorksheet = nil);
// Read an RK value cell
procedure ReadRKValue(AStream: TStream);
// Read the row, column, and XF index at the current stream position
@ -491,6 +498,8 @@ type
procedure ReadVCENTER(AStream: TStream);
// Read WINDOW2 record (gridlines, sheet headers)
procedure ReadWindow2(AStream: TStream); virtual;
// Read WINDOWPROTECT record
procedure ReadWindowProtect(AStream: TStream);
procedure ReadWorkbookGlobals(AStream: TStream); virtual;
procedure ReadWorksheet(AStream: TStream); virtual;
@ -2017,6 +2026,35 @@ begin
// If BIFF5-BIFF8 there is 1 more byte which is not used here.
end;
{@@ ----------------------------------------------------------------------------
Reads the PASSWORD record containing the encrypted password
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadPASSWORD(AStream: TStream;
AWorksheet: TsWorksheet = nil);
var
hash: Word;
cinfo: TsCryptoInfo;
begin
hash := WordLEToN(AStream.ReadWord);
if hash = 0 then
exit; // no password
if AWorksheet = nil then begin
// Password for workbook protection
cinfo := FWorkbook.CryptoInfo;
cinfo.PasswordHash := Format('%.4x', [hash]);
cinfo.Algorithm := caExcel;
FWorkbook.CryptoInfo := cinfo;
end else
begin
// Password for worksheet protection
cinfo := AWorksheet.CryptoInfo;
cinfo.PasswordHash := Format('%.4x', [hash]);
cinfo.Algorithm := caExcel;
AWorksheet.CryptoInfo := cinfo;
end;
end;
{@@ ----------------------------------------------------------------------------
Reads whether the gridlines are printed or not
-------------------------------------------------------------------------------}
@ -2043,6 +2081,28 @@ begin
Options := Options + [poPrintHeaders];
end;
{@@ ----------------------------------------------------------------------------
Determines whether the workbook or worksheet is protected
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadPROTECT(AStream: TStream;
AWorksheet: TsWorksheet = nil);
var
p: Word;
begin
p := WordLEToN(AStream.ReadWord);
if p = 0 then // not protected
exit;
if AWorksheet = nil then
// Workbook protection
FWorkbook.Protection := FWorkbook.Protection + [bpLockStructure]
else begin
// Worksheet protection
AWorksheet.Protection := FWorksheet.Protection + [spCells];
AWorksheet.Protect(true);
end;
end;
{@@ ----------------------------------------------------------------------------
Reads the row, column and xf index
NOT VALID for BIFF2
@ -2875,6 +2935,17 @@ begin
Unused(AStream);
end;
procedure TsSpreadBIFFReader.ReadWindowProtect(AStream: TStream);
var
p: Word;
begin
p := WordLEToN(AStream.ReadWord);
if p = 0 then // Workbook not protected
FWorkbook.Protection := FWorkbook.Protection - [bpLockWindows]
else // Workbook protection
FWorkbook.Protection := FWorkbook.Protection + [bpLockWindows];
end;
procedure TsSpreadBIFFReader.ReadWorksheet(AStream: TStream);
begin
// To be overridden by BIFF5 and BIFF8