fpspreadsheet: Add writing support for cell, sheet and workbook protection in xls files (biff2, biff5, biff8), opendocument and Excel XML files. Not complete, yet.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5795 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2017-03-06 23:30:59 +00:00
parent 20fcc57d97
commit 4505fb76cd
11 changed files with 635 additions and 176 deletions

View File

@@ -10,13 +10,13 @@ type
function AlgorithmToStr(Algorithm: TsCryptoAlgorithm; AUsage: TsAlgorithmUsage): String; function AlgorithmToStr(Algorithm: TsCryptoAlgorithm; AUsage: TsAlgorithmUsage): String;
function StrToAlgorithm(const AName: String): TsCryptoAlgorithm; function StrToAlgorithm(const AName: String): TsCryptoAlgorithm;
(*
function ExcelPasswordHash(const APassword: String): String; function PasswordHash(const APassword: String; Algorithm: TsAlgorithm): String;
*)
implementation implementation
uses uses
LazUTF8; sha1, LazUTF8;
function AlgorithmToStr(Algorithm: TsCryptoAlgorithm; AUsage: TsAlgorithmUsage): String; function AlgorithmToStr(Algorithm: TsCryptoAlgorithm; AUsage: TsAlgorithmUsage): String;
begin begin
@@ -100,5 +100,22 @@ begin
Result := IntToHex(PassHash, 4); Result := IntToHex(PassHash, 4);
end; end;
(*
function SHA1Hash(const AText: String): String;
var
sha1: TSHA1Digest;
begin
sha1 := SHA1String(AText);
Result := PChar(sha1);
end;
function CalcPasswordHash(const APassword: String; Algorithm: TsAlgorithm): String;
begin
case Algorithm of
caExcel: Result := ExcelPasswordHash(APassword);
caSHA1 : Result := SHA1Hash(APassword);
else raise Exception.Create('Hashing algorithm not implemented.');
end;
end; *)
end. end.

View File

@@ -207,6 +207,7 @@ type
function WriteCommentXMLAsString(AComment: String): String; function WriteCommentXMLAsString(AComment: String): String;
function WriteDefaultFontXMLAsString: String; function WriteDefaultFontXMLAsString: String;
function WriteDefaultGraphicStyleXMLAsString: String; overload; function WriteDefaultGraphicStyleXMLAsString: String; overload;
function WriteDocumentProtectionXMLAsString: String;
function WriteFontStyleXMLAsString(const AFormat: TsCellFormat): String; overload; function WriteFontStyleXMLAsString(const AFormat: TsCellFormat): String; overload;
function WriteFontStyleXMLAsString(AFont: TsFont): String; overload; function WriteFontStyleXMLAsString(AFont: TsFont): String; overload;
function WriteHeaderFooterFontXMLAsString(AFont: TsHeaderFooterFont): String; function WriteHeaderFooterFontXMLAsString(AFont: TsHeaderFooterFont): String;
@@ -4996,6 +4997,7 @@ end;
procedure TsSpreadOpenDocWriter.WriteContent; procedure TsSpreadOpenDocWriter.WriteContent;
var var
i: Integer; i: Integer;
s: String;
begin begin
AppendToStream(FSContent, AppendToStream(FSContent,
XML_HEADER); XML_HEADER);
@@ -5089,7 +5091,7 @@ begin
// Body // Body
AppendToStream(FSContent, AppendToStream(FSContent,
'<office:body>' + '<office:body>' +
'<office:spreadsheet>'); '<office:spreadsheet' + WriteDocumentProtectionXMLAsString + '>');
// Write all worksheets // Write all worksheets
for i := 0 to Workbook.GetWorksheetCount - 1 do for i := 0 to Workbook.GetWorksheetCount - 1 do
@@ -6589,6 +6591,29 @@ begin
'style:language-complex="hi" style:country-complex="IN" />'; 'style:language-complex="hi" style:country-complex="IN" />';
end; end;
function TsSpreadOpenDocWriter.WriteDocumentProtectionXMLAsString: String;
var
cinfo: TsCryptoInfo;
pwd, algo: String;
begin
if bpLockStructure in Workbook.Protection then
begin
Result := ' table:structure-protected="true"';
cinfo := Workbook.CryptoInfo;
if cinfo.PasswordHash <> '' then
pwd := Format(' table:protection-key="%s"', [cinfo.PasswordHash])
else
pwd := '';
if cinfo.Algorithm <> caUnknown then
algo := Format(' table:protection-key-digest-algorithm="%s"',
[AlgorithmToStr(cinfo.Algorithm, auOpenDocument)])
else
algo := '';
Result := Result + pwd + algo;
end
else
Result := '';
end;
procedure TsSpreadOpenDocWriter.WriteError(AStream: TStream; procedure TsSpreadOpenDocWriter.WriteError(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell); const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell);
var var

View File

@@ -6763,6 +6763,15 @@ begin
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
end; end;
{@@ ----------------------------------------------------------------------------
Defines how the cell at the specified row and column is protected: lock
cell modification and/or hide formulas. Note that this is activated only after
enabling worksheet protection (worksheet.Protect(true)).
NOTE:
FPSpreadsheet does not enforce these actions. They are only written
to the file for the Office application.
-------------------------------------------------------------------------------}
function TsWorksheet.WriteCellProtection(ARow, ACol: Cardinal; function TsWorksheet.WriteCellProtection(ARow, ACol: Cardinal;
AValue: TsCellProtections): PCell; AValue: TsCellProtections): PCell;
begin begin

View File

@@ -663,9 +663,9 @@ type
spFormatCells, spFormatColumns, spFormatRows, spFormatCells, spFormatColumns, spFormatRows,
spDeleteColumns, spDeleteRows, spDeleteColumns, spDeleteRows,
spInsertColumns, spInsertRows, spInsertHyperlinks, spInsertColumns, spInsertRows, spInsertHyperlinks,
spCells, spSort, spCells, spSort, spObjects,
spSelectLockedCells, spSelectUnlockedCells spSelectLockedCells, spSelectUnlockedCells
{spObjects, spPivotTables, spScenarios } {spPivotTables, spScenarios }
); );
TsWorksheetProtections = set of TsWorksheetProtection; TsWorksheetProtections = set of TsWorksheetProtection;
@@ -676,8 +676,8 @@ type
const const
ALL_SHEET_PROTECTIONS = [spFormatCells, spFormatColumns, spFormatRows, ALL_SHEET_PROTECTIONS = [spFormatCells, spFormatColumns, spFormatRows,
spDeleteColumns, spDeleteRows, spInsertColumns, spInsertRows, spInsertHyperlinks, spDeleteColumns, spDeleteRows, spInsertColumns, spInsertRows, spInsertHyperlinks,
spCells, spSort, spSelectLockedCells, spSelectUnlockedCells spCells, spSort, spObjects, spSelectLockedCells, spSelectUnlockedCells
{, spObjects, spPivotTables, spScenarios} ]; {spPivotTables, spScenarios} ];
DEFAULT_SHEET_PROTECTION = ALL_SHEET_PROTECTIONS - [spSelectLockedCells, spSelectUnlockedcells]; DEFAULT_SHEET_PROTECTION = ALL_SHEET_PROTECTIONS - [spSelectLockedCells, spSelectUnlockedcells];

View File

@@ -63,6 +63,7 @@ type
procedure ReadIXFE(AStream: TStream); procedure ReadIXFE(AStream: TStream);
procedure ReadLabel(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override;
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
procedure ReadPROTECT(AStream: TStream);
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override; procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
procedure ReadRowInfo(AStream: TStream); override; procedure ReadRowInfo(AStream: TStream); override;
function ReadRPNAttr(AStream: TStream; AIdentifier: Byte): Boolean; override; function ReadRPNAttr(AStream: TStream; AIdentifier: Byte): Boolean; override;
@@ -616,10 +617,11 @@ begin
INT_EXCEL_ID_NOTE : ReadComment(AStream); INT_EXCEL_ID_NOTE : ReadComment(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream); INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_OBJECTPROTECT : ReadObjectProtect(AStream);
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet); INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet);
INT_EXCEL_ID_PRINTGRID : ReadPrintGridLines(AStream); INT_EXCEL_ID_PRINTGRID : ReadPrintGridLines(AStream);
INT_EXCEL_ID_PRINTHEADERS : ReadPrintHeaders(AStream); INT_EXCEL_ID_PRINTHEADERS : ReadPrintHeaders(AStream);
INT_EXCEL_ID_PROTECT : ReadPROTECT(AStream, FWorksheet); INT_EXCEL_ID_PROTECT : ReadPROTECT(AStream);
INT_EXCEL_ID_RIGHTMARGIN : ReadMargin(AStream, 1); INT_EXCEL_ID_RIGHTMARGIN : ReadMargin(AStream, 1);
INT_EXCEL_ID_ROW : ReadRowInfo(AStream); INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
INT_EXCEL_ID_SELECTION : ReadSELECTION(AStream); INT_EXCEL_ID_SELECTION : ReadSELECTION(AStream);
@@ -866,6 +868,12 @@ begin
FPendingXFIndex := WordLEToN(AStream.ReadWord); FPendingXFIndex := WordLEToN(AStream.ReadWord);
end; end;
procedure TsSpreadBIFF2Reader.ReadPROTECT(AStream: TStream);
begin
inherited ReadPROTECT(AStream);
FWorksheet.Protect(Workbook.IsProtected);
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Reads the row, column and xf index from the stream Reads the row, column and xf index from the stream
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@@ -1194,8 +1202,8 @@ var
begin begin
fmt := Workbook.GetPointerToCellFormat(AFormatIndex); fmt := Workbook.GetPointerToCellFormat(AFormatIndex);
if fmt^.UsedFormattingFields = [] then begin if (fmt^.UsedFormattingFields = []) and (fmt^.Protection = [cpLockCell]) then begin
Attrib1 := 15; Attrib1 := 15 + $40;
Attrib2 := 0; Attrib2 := 0;
Attrib3 := 0; Attrib3 := 0;
exit; exit;
@@ -1206,6 +1214,8 @@ begin
// Mask $40: 1 = Cell is locked // Mask $40: 1 = Cell is locked
// Mask $80: 1 = Formula is hidden // Mask $80: 1 = Formula is hidden
Attrib1 := Min(XFIndex, $3F) and $3F; Attrib1 := Min(XFIndex, $3F) and $3F;
if cpLockCell in fmt^.Protection then Attrib1 := Attrib1 or $40;
if cpHideFormulas in fmt^.Protection then Attrib1 := Attrib1 or $80;
// 2nd byte: // 2nd byte:
// Mask $3F: Index to FORMAT record ("FORMAT" = number format!) // Mask $3F: Index to FORMAT record ("FORMAT" = number format!)
@@ -1346,13 +1356,17 @@ begin
rec.XFIndex_Locked_Hidden := 0; // to silence the compiler... rec.XFIndex_Locked_Hidden := 0; // to silence the compiler...
FillChar(rec, SizeOf(rec), 0); FillChar(rec, SizeOf(rec), 0);
if fmt^.UsedFormattingFields <> [] then if (fmt^.UsedFormattingFields <> []) or (fmt^.Protection <> [cpLockCell]) then
begin begin
// 1st byte: // 1st byte:
// Mask $3F: Index to XF record // Mask $3F: Index to XF record
// Mask $40: 1 = Cell is locked // Mask $40: 1 = Cell is locked
// Mask $80: 1 = Formula is hidden // Mask $80: 1 = Formula is hidden
rec.XFIndex_Locked_Hidden := Min(XFIndex, $3F) and $3F; rec.XFIndex_Locked_Hidden := Min(XFIndex, $3F) and $3F;
if cpLockCell in fmt^.Protection then
rec.XFIndex_Locked_Hidden := rec.XFIndex_Locked_Hidden or $40;
if cpHideFormulas in fmt^.Protection then
rec.XFIndex_Locked_Hidden := rec.XFIndex_Locked_Hidden or $80;
// 2nd byte: // 2nd byte:
// Mask $3F: Index to FORMAT record // Mask $3F: Index to FORMAT record
@@ -1601,6 +1615,11 @@ begin
WriteFormatCount(AStream); WriteFormatCount(AStream);
WriteNumFormats(AStream); WriteNumFormats(AStream);
if (bpLockStructure in Workbook.Protection) or FWorksheet.IsProtected then
WritePROTECT(AStream, true);
WriteWindowProtect(AStream, bpLockWindows in Workbook.Protection);
WriteObjectProtect(AStream, FWorksheet);
WriteXFRecords(AStream); WriteXFRecords(AStream);
WriteDefaultColWidth(AStream, FWorksheet); WriteDefaultColWidth(AStream, FWorksheet);
WriteColWidths(AStream); WriteColWidths(AStream);
@@ -1700,6 +1719,7 @@ var
rec: TBIFF2_XFRecord; rec: TBIFF2_XFRecord;
b: Byte; b: Byte;
formatIdx, fontIdx: Integer; formatIdx, fontIdx: Integer;
fmtProt: byte;
begin begin
Unused(XFType_Prot); Unused(XFType_Prot);
GetFormatAndFontIndex(AFormatRecord, formatIdx, fontIdx); GetFormatAndFontIndex(AFormatRecord, formatIdx, fontIdx);
@@ -1720,8 +1740,15 @@ begin
5-0 $3F Index to (number) FORMAT record 5-0 $3F Index to (number) FORMAT record
6 $40 1 = Cell is locked 6 $40 1 = Cell is locked
7 $80 1 = Formula is hidden } 7 $80 1 = Formula is hidden }
rec.NumFormat_Prot := WordToLE(formatIdx); fmtProt := formatIdx + $40;
// Cell flags not used, so far... if AFormatRecord <> nil then
begin
if not (cpLockCell in AFormatRecord^.Protection) then
fmtProt := fmtProt and not $40;
if (cpHideFormulas in AFormatRecord^.Protection) then
fmtProt := fmtProt or $80;
end;
rec.NumFormat_Prot := WordToLE(fmtProt);
{Horizontal alignment, border style, and background {Horizontal alignment, border style, and background
Bit Mask Contents Bit Mask Contents

View File

@@ -534,6 +534,7 @@ begin
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
INT_EXCEL_ID_NOTE : ReadComment(AStream); INT_EXCEL_ID_NOTE : ReadComment(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream); INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_OBJECTPROTECT : ReadObjectProtect(AStream, FWorksheet);
INT_EXCEL_ID_PANE : ReadPane(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream); INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream);
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet); INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet);
@@ -1135,6 +1136,8 @@ begin
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
WriteCODEPAGE(AStream, FCodePage); WriteCODEPAGE(AStream, FCodePage);
WriteWindowProtect(AStream, bpLockWindows in Workbook.Protection);
WritePROTECT(AStream, bpLockStructure in Workbook.Protection);
WriteEXTERNCOUNT(AStream); WriteEXTERNCOUNT(AStream);
WriteEXTERNSHEET(AStream); WriteEXTERNSHEET(AStream);
WriteDefinedNames(AStream); WriteDefinedNames(AStream);
@@ -1182,7 +1185,11 @@ begin
WriteMargin(AStream, 2); // 2 = top margin WriteMargin(AStream, 2); // 2 = top margin
WriteMargin(AStream, 3); // 3 = bottom margin WriteMargin(AStream, 3); // 3 = bottom margin
WritePageSetup(AStream); WritePageSetup(AStream);
if FWorksheet.IsProtected then begin
WritePROTECT(AStream, true);
// WriteScenarioProtect(AStream);
WriteObjectProtect(AStream, FWorksheet);
end;
WriteDefaultColWidth(AStream, FWorksheet); WriteDefaultColWidth(AStream, FWorksheet);
WriteColInfos(AStream, FWorksheet); WriteColInfos(AStream, FWorksheet);
WriteDimensions(AStream, FWorksheet); WriteDimensions(AStream, FWorksheet);
@@ -1920,9 +1927,20 @@ begin
rec.NumFormatIndex := WordToLE(j); rec.NumFormatIndex := WordToLE(j);
{ XF type, cell protection and parent style XF } { XF type, cell protection and parent style XF }
rec.XFType_Prot_ParentXF := XFType_Prot and MASK_XF_TYPE_PROT; if AFormatRecord = nil then
if XFType_Prot and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then begin
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_PARENT; rec.XFType_Prot_ParentXF := XFType_Prot and MASK_XF_TYPE_PROT;
if XFType_Prot and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_PARENT;
end else
begin
rec.XFType_Prot_ParentXF := 0;
if cpLockCell in AFormatRecord^.Protection then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_LOCKED;
if cpHideFormulas in AFormatRecord^.Protection then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_FORMULA_HIDDEN;
end;
rec.XFType_Prot_ParentXF := WordToLE(rec.XFType_Prot_ParentXF);
{ Text alignment and text break } { Text alignment and text break }
if AFormatRecord = nil then if AFormatRecord = nil then

View File

@@ -875,6 +875,7 @@ begin
INT_EXCEL_ID_NOTE : ReadNOTE(AStream); INT_EXCEL_ID_NOTE : ReadNOTE(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream); INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_OBJ : ReadOBJ(AStream); INT_EXCEL_ID_OBJ : ReadOBJ(AStream);
INT_EXCEL_ID_OBJECTPROTECT : ReadObjectProtect(AStream, FWorksheet);
INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream); INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet); INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream, FWorksheet);
@@ -2127,7 +2128,9 @@ begin
{ Write workbook globals } { Write workbook globals }
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS); WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
WriteCodePage(AStream, 'ucs2le'); // = utf-16 WriteCodePage(AStream, 'ucs2le'); // = utf-16
WriteWindow1(AStream); WriteWindowProtect(AStream, bpLockWindows in Workbook.Protection);
WritePROTECT(AStream, bpLockStructure in Workbook.Protection);
WriteWINDOW1(AStream);
WriteFonts(AStream); WriteFonts(AStream);
WriteNumFormats(AStream); WriteNumFormats(AStream);
WritePalette(AStream); WritePalette(AStream);
@@ -2174,7 +2177,11 @@ begin
WriteMargin(AStream, 2); // 2 = top margin WriteMargin(AStream, 2); // 2 = top margin
WriteMargin(AStream, 3); // 3 = bottom margin WriteMargin(AStream, 3); // 3 = bottom margin
WritePageSetup(AStream); WritePageSetup(AStream);
if FWorksheet.IsProtected then begin
WritePROTECT(AStream, true);
// WriteScenarioProtect(AStream);
WriteObjectProtect(AStream, FWorksheet);
end;
WriteDefaultColWidth(AStream, FWorksheet); WriteDefaultColWidth(AStream, FWorksheet);
WriteColInfos(AStream, FWorksheet); WriteColInfos(AStream, FWorksheet);
WriteDimensions(AStream, FWorksheet); WriteDimensions(AStream, FWorksheet);
@@ -3657,9 +3664,19 @@ begin
rec.NumFormatIndex := WordToLE(j); rec.NumFormatIndex := WordToLE(j);
{ XF type, cell protection and parent style XF } { XF type, cell protection and parent style XF }
rec.XFType_Prot_ParentXF := XFType_Prot and MASK_XF_TYPE_PROT; if AFormatRecord = nil then begin
if XFType_Prot and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then rec.XFType_Prot_ParentXF := XFType_Prot and MASK_XF_TYPE_PROT;
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_PARENT; if XFType_Prot and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_PARENT;
end else
begin
rec.XFType_Prot_ParentXF := 0;
if cpLockCell in AFormatRecord^.Protection then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_LOCKED;
if cpHideFormulas in AFormatRecord^.Protection then
rec.XFType_Prot_ParentXF := rec.XFType_Prot_ParentXF or MASK_XF_TYPE_PROT_FORMULA_HIDDEN;
end;
rec.XFType_Prot_ParentXF := WordToLE(rec.XFType_Prot_ParentXF);
{ Text alignment and text break } { Text alignment and text break }
if AFormatRecord = nil then if AFormatRecord = nil then
@@ -3708,12 +3725,12 @@ begin
{ Used attributes } { Used attributes }
rec.UsedAttrib := rec.UsedAttrib :=
MASK_XF_USED_ATTRIB_NUMBER_FORMAT or MASK_XF_USED_ATTRIB_NUMBER_FORMAT or
MASK_XF_USED_ATTRIB_FONT or MASK_XF_USED_ATTRIB_FONT or
MASK_XF_USED_ATTRIB_TEXT or MASK_XF_USED_ATTRIB_TEXT or
MASK_XF_USED_ATTRIB_BORDER_LINES or MASK_XF_USED_ATTRIB_BORDER_LINES or
MASK_XF_USED_ATTRIB_BACKGROUND or MASK_XF_USED_ATTRIB_BACKGROUND or
MASK_XF_USED_ATTRIB_CELL_PROTECTION; MASK_XF_USED_ATTRIB_CELL_PROTECTION;
{ Cell border lines and background area } { Cell border lines and background area }

View File

@@ -37,6 +37,7 @@ const
INT_EXCEL_ID_PANE = $0041; INT_EXCEL_ID_PANE = $0041;
INT_EXCEL_ID_CODEPAGE = $0042; INT_EXCEL_ID_CODEPAGE = $0042;
INT_EXCEL_ID_DEFCOLWIDTH = $0055; INT_EXCEL_ID_DEFCOLWIDTH = $0055;
INT_EXCEL_ID_OBJECTPROTECT = $0063;
{ RECORD IDs which did not changed across versions 2-5 } { 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
@@ -133,7 +134,7 @@ const
{ XF_TYPE_PROT - XF Type and Cell protection (3 Bits) - BIFF3-BIFF8 } { XF_TYPE_PROT - XF Type and Cell protection (3 Bits) - BIFF3-BIFF8 }
MASK_XF_TYPE_PROT_LOCKED = $0001; MASK_XF_TYPE_PROT_LOCKED = $0001;
MASK_XF_TYPE_PROT_FORMULA_HIDDEN = $0002; MASK_XF_TYPE_PROT_FORMULA_HIDDEN = $0002;
MASK_XF_TYPE_PROT_STYLE_XF = $0004; // 0 = CELL XF MASK_XF_TYPE_PROT_STYLE_XF = $0005; // was: 4 (wp)
MASK_XF_TYPE_PROTECTION = $0007; MASK_XF_TYPE_PROTECTION = $0007;
@@ -450,6 +451,8 @@ type
procedure ReadMulRKValues(const AStream: TStream); procedure ReadMulRKValues(const AStream: TStream);
// Read floating point number // Read floating point number
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
// Read OBJECTPROTECT record
procedure ReadObjectProtect(AStream: TStream; AWorksheet: TsWorksheet = nil);
// Read palette // Read palette
procedure ReadPalette(AStream: TStream); procedure ReadPalette(AStream: TStream);
// Read page setup // Read page setup
@@ -585,6 +588,8 @@ type
// Writes out a floating point NUMBER record // Writes out a floating point NUMBER record
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Double; ACell: PCell); override; const AValue: Double; ACell: PCell); override;
// Writes an OBJECTPROTECT record
procedure WriteObjectProtect(AStream: TStream; ASheet: TsWorksheet);
procedure WritePageSetup(AStream: TStream); procedure WritePageSetup(AStream: TStream);
// Writes out a PALETTE record containing all colors defined in the workbook // Writes out a PALETTE record containing all colors defined in the workbook
procedure WritePalette(AStream: TStream); procedure WritePalette(AStream: TStream);
@@ -594,6 +599,7 @@ type
// Writes out whether grid lines are printed // Writes out whether grid lines are printed
procedure WritePrintGridLines(AStream: TStream); procedure WritePrintGridLines(AStream: TStream);
procedure WritePrintHeaders(AStream: TStream); procedure WritePrintHeaders(AStream: TStream);
procedure WritePROTECT(AStream: TStream; AEnable: Boolean);
// Writes out a ROW record // Writes out a ROW record
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet; procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual; ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual;
@@ -632,6 +638,7 @@ type
procedure WriteVirtualCells(AStream: TStream; ASheet: TsWorksheet); procedure WriteVirtualCells(AStream: TStream; ASheet: TsWorksheet);
// Writes out a WINDOW1 record // Writes out a WINDOW1 record
procedure WriteWindow1(AStream: TStream); virtual; procedure WriteWindow1(AStream: TStream); virtual;
procedure WriteWindowProtect(AStream: TStream; AEnable: Boolean);
// Writes an XF record // Writes an XF record
procedure WriteXF(AStream: TStream; ACellFormat: PsCellFormat; procedure WriteXF(AStream: TStream; ACellFormat: PsCellFormat;
XFType_Prot: Byte = 0); virtual; XFType_Prot: Byte = 0); virtual;
@@ -1903,6 +1910,24 @@ begin
Workbook.OnReadCellData(Workbook, ARow, ACol, cell); Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
end; end;
{@@ ----------------------------------------------------------------------------
Reads the OBJECTPROTECT record. It determines whether the objects (drawings,
etc) of the current sheet are protected.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadObjectProtect(AStream: TStream;
AWorksheet: TsWorksheet = nil);
var
w: Word;
sp: TsWorksheetProtections;
begin
if AWorksheet = nil then
AWorksheet := FWorksheet;
w := WordLEToN(AStream.ReadWord);
sp := AWorksheet.Protection;
if w = 0 then Exclude(sp, spObjects) else Include(sp, spObjects);
AWorksheet.Protection := sp;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Reads the color palette Reads the color palette
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@@ -2092,16 +2117,22 @@ var
p: Word; p: Word;
begin begin
p := WordLEToN(AStream.ReadWord); p := WordLEToN(AStream.ReadWord);
if p = 0 then // not protected
exit;
if AWorksheet = nil then if AWorksheet = nil then
begin
// Workbook protection // Workbook protection
FWorkbook.Protection := FWorkbook.Protection + [bpLockStructure] if p = 1 then
else begin FWorkbook.Protection := FWorkbook.Protection + [bpLockStructure]
else
FWorkbook.Protection := FWorkbook.Protection - [bpLockStructure];
end else
begin
// Worksheet protection // Worksheet protection
AWorksheet.Protection := FWorksheet.Protection + [spCells]; if p = 1 then begin
AWorksheet.Protect(true); AWorksheet.Protection := AWorksheet.Protection + [spCells];
AWorksheet.Protect(true);
end else
AWorksheet.Protect(false);
end; end;
end; end;
@@ -3833,6 +3864,24 @@ begin
end; end;
end; end;
{@@ ----------------------------------------------------------------------------
Writes an OBJECTPROTECT record. It determines whether the objects (drawings,
etc.) of the current worksheet are protected. Omitted if object
protection is not active.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteObjectProtect(AStream: TStream;
ASheet: TsWorksheet);
var
w: Word;
begin
if not ASheet.IsProtected then
exit;
WriteBIFFHeader(AStream, INT_EXCEL_ID_OBJECTPROTECT, 2);
w := IfThen(spObjects in ASheet.Protection, 1, 0);
AStream.WriteWord(WordToLE(w));
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes a PAGESETUP record containing information on printing Writes a PAGESETUP record containing information on printing
Valid for BIFF5-8 Valid for BIFF5-8
@@ -4019,6 +4068,125 @@ begin
AStream.WriteWord(WordToLE(w)); AStream.WriteWord(WordToLE(w));
end; end;
{@@ ----------------------------------------------------------------------------
Writes an Excel PROTECT record
Valid for BIFF2-BIFF8
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WritePROTECT(AStream: TStream; AEnable: Boolean);
var
w: Word;
begin
{ BIFF Header }
WriteBiffHeader(AStream, INT_EXCEL_ID_PROTECT, 2);
w := IfThen(AEnable, 1, 0);
AStream.WriteWord(WordToLE(w));
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;
fmt: PsCellFormat;
begin
if (ARowIndex >= FLimitations.MaxRowCount) or
(AFirstColIndex >= FLimitations.MaxColCount) or
(ALastColIndex >= FLimitations.MaxColCount)
then
exit;
// 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) then
begin
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if (uffBorder in fmt^.UsedFormattingFields) then
begin
if (cbNorth in fmt^.Border) and (fmt^.BorderStyles[cbNorth].LineStyle = lsThick)
then spaceabove := true;
if (cbSouth in fmt^.Border) and (fmt^.BorderStyles[cbSouth].LineStyle = lsThick)
then spacebelow := true;
end;
end;
if spaceabove and spacebelow then break;
inc(colindex);
end;
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_ROW, 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^.RowHeightType = rhtDefault) then
rowheight := PtsToTwips(ASheet.ReadDefaultRowHeight(suPoints))
else
rowheight := PtsToTwips(FWorkbook.ConvertUnits(ARow^.Height, FWorkbook.Units, suPoints));
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) and (ARow^.RowHeightType = rhtCustom) then // Custom row height
dw := dw or $00000040; // Row height and font height do not match
if ARow^.FormatIndex > 0 then begin
dw := dw or $00000080; // Row has custom format
dw := dw or DWord(FindXFIndex(ARow^.FormatIndex) shl 16); // xf index
end;
{ Write out }
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.Cells.GetFirstCellOfRow(row^.Row);
if cell1 <> nil then begin
cell2 := ASheet.Cells.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 the address of a cell as used in an RPN formula and returns the Writes the address of a cell as used in an RPN formula and returns the
count of bytes written. count of bytes written.
@@ -4421,110 +4589,6 @@ begin
AStream.WriteWord(WordToLE(ASize)); AStream.WriteWord(WordToLE(ASize));
end; 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;
fmt: PsCellFormat;
begin
if (ARowIndex >= FLimitations.MaxRowCount) or
(AFirstColIndex >= FLimitations.MaxColCount) or
(ALastColIndex >= FLimitations.MaxColCount)
then
exit;
// 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) then
begin
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if (uffBorder in fmt^.UsedFormattingFields) then
begin
if (cbNorth in fmt^.Border) and (fmt^.BorderStyles[cbNorth].LineStyle = lsThick)
then spaceabove := true;
if (cbSouth in fmt^.Border) and (fmt^.BorderStyles[cbSouth].LineStyle = lsThick)
then spacebelow := true;
end;
end;
if spaceabove and spacebelow then break;
inc(colindex);
end;
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_ROW, 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^.RowHeightType = rhtDefault) then
rowheight := PtsToTwips(ASheet.ReadDefaultRowHeight(suPoints))
else
rowheight := PtsToTwips(FWorkbook.ConvertUnits(ARow^.Height, FWorkbook.Units, suPoints));
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) and (ARow^.RowHeightType = rhtCustom) then // Custom row height
dw := dw or $00000040; // Row height and font height do not match
if ARow^.FormatIndex > 0 then begin
dw := dw or $00000080; // Row has custom format
dw := dw or DWord(FindXFIndex(ARow^.FormatIndex) shl 16); // xf index
end;
{ Write out }
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.Cells.GetFirstCellOfRow(row^.Row);
if cell1 <> nil then begin
cell2 := ASheet.Cells.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 the SCL record - this is the current magnification factor of the sheet Writes the SCL record - this is the current magnification factor of the sheet
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@@ -4898,6 +4962,18 @@ begin
AStream.WriteWord(WordToLE(600)); AStream.WriteWord(WordToLE(600));
end; end;
procedure TsSpreadBIFFWriter.WriteWindowProtect(AStream: TStream;
AEnable: Boolean);
var
w: Word;
begin
{ BIFF Header }
WriteBiffHeader(AStream, INT_EXCEL_ID_WINDOWPROTECT, 2);
w := IfThen(AEnable, 1, 0);
AStream.WriteWord(WordToLE(w));
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes an XF record needed for cell formatting. Writes an XF record needed for cell formatting.
Is called from WriteXFRecords. Is called from WriteXFRecords.
@@ -4949,7 +5025,7 @@ begin
// XF14 // XF14
WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF); WriteXF(AStream, nil, MASK_XF_TYPE_PROT_STYLE_XF);
// XF15 - Default, no formatting // XF15 - Default, no formatting
WriteXF(AStream, nil, 0); WriteXF(AStream, nil, MASK_XF_TYPE_PROT_LOCKED);
// Add all further non-standard format records // Add all further non-standard format records
// The first style was already added --> begin loop with 1 // The first style was already added --> begin loop with 1

View File

@@ -143,6 +143,8 @@ const
2 2
); );
FALSE_TRUE: array[boolean] of string = ('False', 'True');
function GetCellContentTypeStr(ACell: PCell): String; function GetCellContentTypeStr(ACell: PCell): String;
begin begin
case ACell^.ContentType of case ACell^.ContentType of
@@ -428,16 +430,23 @@ end;
procedure TsSpreadExcelXMLWriter.WriteExcelWorkbook(AStream: TStream); procedure TsSpreadExcelXMLWriter.WriteExcelWorkbook(AStream: TStream);
var var
datemodeStr: String; datemodeStr: String;
protectStr: String;
begin begin
if FDateMode = dm1904 then if FDateMode = dm1904 then
datemodeStr := INDENT2 + '<Date1904/>' + LF else datemodeStr := INDENT2 + '<Date1904/>' + LF else
datemodeStr := ''; datemodeStr := '';
protectStr := Format(
'<ProtectStructure>%s</ProtectStructure>' + LF + INDENT2 +
'<ProtectWindows>%s</ProtectWindows>' + LF, [
FALSE_TRUE[bpLockStructure in Workbook.Protection],
FALSE_TRUE[bpLockWindows in Workbook.Protection]
]);
AppendToStream(AStream, INDENT1 + AppendToStream(AStream, INDENT1 +
'<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">' + LF + '<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">' + LF +
datemodeStr + INDENT2 + datemodeStr + INDENT2 +
'<ProtectStructure>False</ProtectStructure>' + LF + INDENT2 + protectStr + INDENT1 +
'<ProtectWindows>False</ProtectWindows>' + LF + INDENT1 +
'</ExcelWorkbook>' + LF); '</ExcelWorkbook>' + LF);
end; end;
@@ -653,6 +662,18 @@ begin
'</Borders>' + LF); '</Borders>' + LF);
end; end;
// Protection
s := '';
if FWorkbook.IsProtected then begin
if not (cpLockCell in fmt^.Protection) then
s := s + 'ss:Protected="0" ';
if cpHideFormulas in fmt^.Protection then
s := s + 'x:HideFormula="1" ';
end;
if s <> '' then
AppendToStream(AStream, INDENT3 +
'<Protection ' + s + '/>' + LF);
AppendToStream(AStream, INDENT2 + AppendToStream(AStream, INDENT2 +
'</Style>' + LF); '</Style>' + LF);
end; end;
@@ -784,10 +805,20 @@ end;
procedure TsSpreadExcelXMLWriter.WriteWorksheet(AStream: TStream; procedure TsSpreadExcelXMLWriter.WriteWorksheet(AStream: TStream;
AWorksheet: TsWorksheet); AWorksheet: TsWorksheet);
var
protectedStr: String;
begin begin
FWorksheet := AWorksheet; FWorksheet := AWorksheet;
if FWorksheet.IsProtected then
protectedStr := ' ss:Protected="1"' else
protectedStr := '';
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
' <Worksheet ss:Name="%s">' + LF, [UTF8TextToXMLText(AWorksheet.Name)]) ); ' <Worksheet ss:Name="%s"%s>' + LF, [
UTF8TextToXMLText(AWorksheet.Name),
protectedStr
]) );
WriteTable(AStream, AWorksheet); WriteTable(AStream, AWorksheet);
WriteWorksheetOptions(AStream, AWorksheet); WriteWorksheetOptions(AStream, AWorksheet);
AppendToStream(AStream, AppendToStream(AStream,
@@ -805,6 +836,7 @@ var
layoutStr: String; layoutStr: String;
marginStr: String; marginStr: String;
selectedStr: String; selectedStr: String;
protectStr: String;
begin begin
// Orientation, some PageLayout.Options // Orientation, some PageLayout.Options
layoutStr := GetLayoutStr(AWorksheet); layoutStr := GetLayoutStr(AWorksheet);
@@ -839,6 +871,13 @@ begin
// Frozen panes // Frozen panes
frozenStr := GetFrozenPanesStr(AWorksheet, INDENT3); frozenStr := GetFrozenPanesStr(AWorksheet, INDENT3);
// Protection
protectStr := Format(INDENT3 + '<ProtectObjects>%s</ProtectObjects>' + LF +
INDENT3 + '<ProtectScenarios>%s</ProtectScenarios>' + LF, [
AWorksheet.IsProtected and (spObjects in AWorksheet.Protection),
AWorksheet.IsProtected {and [spScenarios in AWorksheet.Protection])}
]);
// Put it all together... // Put it all together...
AppendToStream(AStream, INDENT2 + AppendToStream(AStream, INDENT2 +
'<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">' + LF + INDENT3 + '<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">' + LF + INDENT3 +
@@ -849,6 +888,7 @@ begin
marginStr + INDENT3 + marginStr + INDENT3 +
'</PageSetup>' + LF + '</PageSetup>' + LF +
selectedStr + selectedStr +
protectStr +
frozenStr + frozenStr +
hideGridStr + hideGridStr +
hideHeadersStr + INDENT2 + hideHeadersStr + INDENT2 +

View File

@@ -2051,12 +2051,12 @@ begin
if (s = '1') then Include(shp, spSelectUnlockedCells) else if (s = '1') then Include(shp, spSelectUnlockedCells) else
if (s = '') or (s = '0') then Exclude(shp, spSelectUnlockedCells); if (s = '') or (s = '0') then Exclude(shp, spSelectUnlockedCells);
// these options are currently not supported by fpspreadsheet
{
s := GetAttrValue(ANode, 'objects'); s := GetAttrValue(ANode, 'objects');
if (s = '1') then Include(shp, spObjects) else if (s = '1') then Include(shp, spObjects) else
if (s = '') or (s = '0') then Exclude(shp, spObjects); if (s = '') or (s = '0') then Exclude(shp, spObjects);
// these options are currently not supported by fpspreadsheet
{
s := GetAttrValue(ANode, 'scenarios'); s := GetAttrValue(ANode, 'scenarios');
if (s = '1') then Include(shp, spScenarios) else if (s = '1') then Include(shp, spScenarios) else
if (s = '') or (s = '0') then Exclude(shp, spScenarios); if (s = '') or (s = '0') then Exclude(shp, spScenarios);
@@ -3423,7 +3423,7 @@ begin
// No attribute -> attr="0" // No attribute -> attr="0"
if AWorksheet.IsProtected then if AWorksheet.IsProtected then
s := ' sheet="1" objects="1" scenarios="1"' s := ' sheet="1" scenarios="1"'
else else
Exit; //exit if sheet not protected Exit; //exit if sheet not protected
@@ -3445,11 +3445,11 @@ begin
end; end;
end; end;
{ if spObjects in AWorksheet.Protection then
if spObjects in AWorksheet.Protection then // to do: Remove from default above
s := s + ' objects="1"'; s := s + ' objects="1"';
if spScenarios in AWorksheet.Protection then {
if spScenarios in AWorksheet.Protection then // to do: Remove from default above
s := s + ' scenarios="1"'; s := s + ' scenarios="1"';
} }

View File

@@ -32,29 +32,71 @@ type
published published
// Writes out protection & reads back. // Writes out protection & reads back.
{ BIFF2 protection tests }
procedure TestWriteRead_BIFF2_WorkbookProtection_None;
procedure TestWriteRead_BIFF2_WorkbookProtection_Struct;
procedure TestWriteRead_BIFF2_WorkbookProtection_Win;
procedure TestWriteRead_BIFF2_WorkbookProtection_StructWin;
procedure TestWriteRead_BIFF2_WorksheetProtection_Default;
procedure TestWriteRead_BIFF2_WorksheetProtection_Objects;
procedure TestWriteRead_BIFF2_CellProtection;
{ BIFF5 protection tests }
procedure TestWriteRead_BIFF5_WorkbookProtection_None;
procedure TestWriteRead_BIFF5_WorkbookProtection_Struct;
procedure TestWriteRead_BIFF5_WorkbookProtection_Win;
procedure TestWriteRead_BIFF5_WorkbookProtection_StructWin;
procedure TestWriteRead_BIFF5_WorksheetProtection_Default;
procedure TestWriteRead_BIFF5_WorksheetProtection_SelectLockedCells;
procedure TestWriteRead_BIFF5_WorksheetProtection_SelectUnlockedCells;
procedure TestWriteRead_BIFF5_WorksheetProtection_Objects;
procedure TestWriteRead_BIFF5_CellProtection;
{ BIFF8 protection tests }
procedure TestWriteRead_BIFF8_WorkbookProtection_None;
procedure TestWriteRead_BIFF8_WorkbookProtection_Struct;
procedure TestWriteRead_BIFF8_WorkbookProtection_Win;
procedure TestWriteRead_BIFF8_WorkbookProtection_StructWin;
procedure TestWriteRead_BIFF8_WorksheetProtection_Default;
procedure TestWriteRead_BIFF8_WorksheetProtection_SelectLockedCells;
procedure TestWriteRead_BIFF8_WorksheetProtection_SelectUnlockedCells;
procedure TestWriteRead_BIFF8_WorksheetProtection_Objects;
procedure TestWriteRead_BIFF8_CellProtection;
{ OOXML protection tests } { OOXML protection tests }
procedure TestWriteRead_OOXML_WorkbookProtection_None; procedure TestWriteRead_OOXML_WorkbookProtection_None;
procedure TestWriteRead_OOXML_WorkbookProtection_Struct; procedure TestWriteRead_OOXML_WorkbookProtection_Struct;
procedure TestWriteRead_OOXML_WorkbookProtection_Win; procedure TestWriteRead_OOXML_WorkbookProtection_Win;
procedure TestWriteRead_OOXML_WorkbookProtection_StructWin; procedure TestWriteRead_OOXML_WorkbookProtection_StructWin;
procedure TestWriteRead_OOXML_WorksheetProtection_None; procedure TestWriteRead_OOXML_WorksheetProtection_Default;
procedure TestWriteRead_OOXML_WorksheetProtection_FormatCells; procedure TestWriteRead_OOXML_WorksheetProtection_FormatCells;
procedure TestWriteRead_OOXML_WorksheetProtection_FormatColumns; procedure TestWriteRead_OOXML_WorksheetProtection_FormatColumns;
procedure TestWriteRead_OOXML_WorksheetProtection_FormatRows; procedure TestWriteRead_OOXML_WorksheetProtection_FormatRows;
procedure TestWriteRead_OOXML_WorksheetProtection_DeleteColumns; procedure TestWriteRead_OOXML_WorksheetProtection_DeleteColumns;
procedure TestWriteRead_OOXML_WorksheetProtection_DeleteRows; procedure TestWriteRead_OOXML_WorksheetProtection_DeleteRows;
procedure TestWriteRead_OOXML_WorksheetProtection_InsertColumns; procedure TestWriteRead_OOXML_WorksheetProtection_InsertColumns;
procedure TestWriteRead_OOXML_WorksheetProtection_InsertRows;
procedure TestWriteRead_OOXML_WorksheetProtection_InsertHyperlinks; procedure TestWriteRead_OOXML_WorksheetProtection_InsertHyperlinks;
procedure TestWriteRead_OOXML_WorksheetProtection_Sheet; procedure TestWriteRead_OOXML_WorksheetProtection_InsertRows;
procedure TestWriteRead_OOXML_WorksheetProtection_Sort; procedure TestWriteRead_OOXML_WorksheetProtection_Sort;
procedure TestWriteRead_OOXML_WorksheetProtection_SelectLockedCells; procedure TestWriteRead_OOXML_WorksheetProtection_SelectLockedCells;
procedure TestWriteRead_OOXML_WorksheetProtection_SelectUnlockedCells; procedure TestWriteRead_OOXML_WorksheetProtection_SelectUnlockedCells;
procedure TestWriteRead_OOXML_WorksheetProtection_All; procedure TestWriteRead_OOXML_WorksheetProtection_Objects;
procedure TestWriteRead_OOXML_CellProtection; procedure TestWriteRead_OOXML_CellProtection;
{ ODS protection tests }
procedure TestWriteRead_ODS_WorkbookProtection_None;
procedure TestWriteRead_ODS_WorkbookProtection_Struct;
//procedure TestWriteRead_ODS_WorkbookProtection_Win;
//procedure TestWriteRead_ODS_WorkbookProtection_StructWin;
end; end;
implementation implementation
@@ -126,8 +168,8 @@ const
ALL_SHEET_PROTECTIONS = [ ALL_SHEET_PROTECTIONS = [
spFormatCells, spFormatColumns, spFormatRows, spFormatCells, spFormatColumns, spFormatRows,
spDeleteColumns, spDeleteRows, spInsertColumns, spInsertRows, spDeleteColumns, spDeleteRows, spInsertColumns, spInsertRows,
spInsertHyperlinks, spSort, spSelectLockedCells, spInsertHyperlinks, spSort, spObjects,
spSelectUnlockedCells spSelectLockedCells, spSelectUnlockedCells
]; // NOTE: spCells is handled separately ]; // NOTE: spCells is handled separately
var var
MyWorkbook: TsWorkbook; MyWorkbook: TsWorkbook;
@@ -141,6 +183,23 @@ begin
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorksheet := MyWorkBook.AddWorksheet(ProtectionSheet); MyWorksheet := MyWorkBook.AddWorksheet(ProtectionSheet);
expected := DEFAULT_SHEET_PROTECTION;
case ACondition of
0: ;
1: Exclude(expected, spFormatCells);
2: Exclude(expected, spFormatColumns);
3: Exclude(expected, spFormatRows);
4: Exclude(expected, spDeleteColumns);
5: Exclude(expected, spDeleteRows);
6: Exclude(expected, spInsertColumns);
7: Exclude(expected, spInsertHyperlinks);
8: Exclude(expected, spInsertRows);
9: Exclude(expected, spSort);
10: Exclude(expected, spSelectLockedCells);
11: Exclude(expected, spSelectUnlockedCells);
12: Exclude(expected, spObjects);
end;
{
case ACondition of case ACondition of
0: expected := []; 0: expected := [];
1: expected := [spFormatCells]; 1: expected := [spFormatCells];
@@ -154,8 +213,10 @@ begin
9: expected := [spSort]; 9: expected := [spSort];
10: expected := [spSelectLockedCells]; 10: expected := [spSelectLockedCells];
11: expected := [spSelectUnlockedCells]; 11: expected := [spSelectUnlockedCells];
12: expected := ALL_SHEET_PROTECTIONS; 12: expected := [spObjects];
13: expected := ALL_SHEET_PROTECTIONS;
end; end;
}
MyWorksheet.Protection := expected; MyWorksheet.Protection := expected;
MyWorksheet.Protect(true); MyWorksheet.Protect(true);
MyWorkBook.WriteToFile(TempFile, AFormat, true); MyWorkBook.WriteToFile(TempFile, AFormat, true);
@@ -172,11 +233,11 @@ begin
fail(msg + 'Sheet protection not active'); fail(msg + 'Sheet protection not active');
actual := MyWorksheet.Protection; actual := MyWorksheet.Protection;
if actual <> [] then actual := actual - [spCells]; // if actual <> [] then actual := actual - [spCells];
msg := 'Test saved worksheet protection mismatch: '; msg := 'Test saved worksheet protection mismatch: ';
if actual <> expected then if actual <> expected then
case ACondition of case ACondition of
0: fail(msg + 'no protection'); 0: fail(msg + 'default protection');
1: fail(msg + 'spFormatCells'); 1: fail(msg + 'spFormatCells');
2: fail(msg + 'spFormatColumns'); 2: fail(msg + 'spFormatColumns');
3: fail(msg + 'spFormatRows'); 3: fail(msg + 'spFormatRows');
@@ -188,7 +249,7 @@ begin
9: fail(msg + 'spSort'); 9: fail(msg + 'spSort');
10: fail(msg + 'spSelectLockedCells'); 10: fail(msg + 'spSelectLockedCells');
11: fail(msg + 'spSelectUnlockedCells'); 11: fail(msg + 'spSelectUnlockedCells');
12: fail(msg + 'all options'); 12: fail(msg + 'spObjects');
end; end;
finally finally
@@ -212,17 +273,19 @@ begin
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorksheet := MyWorkBook.AddWorksheet(ProtectionSheet); MyWorksheet := MyWorkBook.AddWorksheet(ProtectionSheet);
// A1 --> lock cell
cell := Myworksheet.WriteText(0, 0, 'Protected'); cell := Myworksheet.WriteText(0, 0, 'Protected');
MyWorksheet.WriteCellProtection(cell, [cpLockCell]); MyWorksheet.WriteCellProtection(cell, [cpLockCell]);
// B1 --> not protected at all
cell := MyWorksheet.WriteText(1, 0, 'Not protected'); cell := MyWorksheet.WriteText(1, 0, 'Not protected');
MyWorksheet.WriteCellProtection(cell, []); MyWorksheet.WriteCellProtection(cell, []);
// A2 --> lock cell & hide formulas
cell := Myworksheet.WriteFormula(0, 1, '=A1'); cell := Myworksheet.WriteFormula(0, 1, '=A1');
MyWorksheet.WriteCellProtection(cell, [cpLockCell, cpHideFormulas]); MyWorksheet.WriteCellProtection(cell, [cpLockCell, cpHideFormulas]);
// B2 --> hide formula only
cell := MyWorksheet.WriteFormula(1, 1, '=A2'); cell := MyWorksheet.WriteFormula(1, 1, '=A2');
Myworksheet.WriteCellProtection(Cell, [cpHideFormulas]); Myworksheet.WriteCellProtection(Cell, [cpHideFormulas]);
MyWorksheet.Protect(true); MyWorksheet.Protect(true);
// NOTE: FPSpreadsheet does not enforce these actions. They are only written
// to the file for the Office application.
MyWorkBook.WriteToFile(TempFile, AFormat, true); MyWorkBook.WriteToFile(TempFile, AFormat, true);
finally finally
MyWorkbook.Free; MyWorkbook.Free;
@@ -271,7 +334,153 @@ begin
end; end;
{ Tests for OOXML file format } {------------------------------------------------------------------------------}
{ Tests for BIFF2 file format }
{------------------------------------------------------------------------------}
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_WorkbookProtection_None;
begin
TestWriteRead_WorkbookProtection(sfExcel2, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_WorkbookProtection_Struct;
begin
TestWriteRead_WorkbookProtection(sfExcel2, 1);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_WorkbookProtection_Win;
begin
TestWriteRead_WorkbookProtection(sfExcel2, 2);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_WorkbookProtection_StructWin;
begin
TestWriteRead_WorkbookProtection(sfExcel2, 3);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_WorksheetProtection_Default;
begin
TestWriteRead_WorksheetProtection(sfExcel2, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_WorksheetProtection_Objects;
begin
TestWriteRead_WorksheetProtection(sfExcel2, 12);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF2_CellProtection;
begin
TestWriteRead_CellProtection(sfExcel2);
end;
{------------------------------------------------------------------------------}
{ Tests for BIFF5 file format }
{------------------------------------------------------------------------------}
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorkbookProtection_None;
begin
TestWriteRead_WorkbookProtection(sfExcel5, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorkbookProtection_Struct;
begin
TestWriteRead_WorkbookProtection(sfExcel5, 1);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorkbookProtection_Win;
begin
TestWriteRead_WorkbookProtection(sfExcel5, 2);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorkbookProtection_StructWin;
begin
TestWriteRead_WorkbookProtection(sfExcel5, 3);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorksheetProtection_Default;
begin
TestWriteRead_WorksheetProtection(sfExcel5, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorksheetProtection_SelectLockedCells;
begin
TestWriteRead_WorksheetProtection(sfExcel5, 10);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorksheetProtection_SelectUnlockedCells;
begin
TestWriteRead_WorksheetProtection(sfExcel5, 11);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_WorksheetProtection_Objects;
begin
TestWriteRead_WorksheetProtection(sfExcel5, 12);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF5_CellProtection;
begin
TestWriteRead_CellProtection(sfExcel5);
end;
{------------------------------------------------------------------------------}
{ Tests for BIFF8 file format }
{------------------------------------------------------------------------------}
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorkbookProtection_None;
begin
TestWriteRead_WorkbookProtection(sfExcel8, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorkbookProtection_Struct;
begin
TestWriteRead_WorkbookProtection(sfExcel8, 1);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorkbookProtection_Win;
begin
TestWriteRead_WorkbookProtection(sfExcel8, 2);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorkbookProtection_StructWin;
begin
TestWriteRead_WorkbookProtection(sfExcel8, 3);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorksheetProtection_Default;
begin
TestWriteRead_WorksheetProtection(sfExcel8, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorksheetProtection_SelectLockedCells;
begin
TestWriteRead_WorksheetProtection(sfExcel8, 10);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorksheetProtection_SelectUnlockedCells;
begin
TestWriteRead_WorksheetProtection(sfExcel8, 11);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_WorksheetProtection_Objects;
begin
TestWriteRead_WorksheetProtection(sfExcel8, 12);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_BIFF8_CellProtection;
begin
TestWriteRead_CellProtection(sfExcel8);
end;
{------------------------------------------------------------------------------}
{ Tests for OOXML file format }
{------------------------------------------------------------------------------}
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorkbookProtection_None; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorkbookProtection_None;
begin begin
TestWriteRead_WorkbookProtection(sfOOXML, 0); TestWriteRead_WorkbookProtection(sfOOXML, 0);
@@ -292,7 +501,7 @@ begin
TestWriteRead_WorkbookProtection(sfOOXML, 3); TestWriteRead_WorkbookProtection(sfOOXML, 3);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_None; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_Default;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 0); TestWriteRead_WorksheetProtection(sfOOXML, 0);
end; end;
@@ -327,46 +536,67 @@ begin
TestWriteRead_WorksheetProtection(sfOOXML, 6); TestWriteRead_WorksheetProtection(sfOOXML, 6);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_InsertRows; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_InsertHyperlinks;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 7); TestWriteRead_WorksheetProtection(sfOOXML, 7);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_InsertHyperlinks; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_InsertRows;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 8); TestWriteRead_WorksheetProtection(sfOOXML, 8);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_Sheet; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_Sort;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 9); TestWriteRead_WorksheetProtection(sfOOXML, 9);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_Sort; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_SelectLockedCells;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 10); TestWriteRead_WorksheetProtection(sfOOXML, 10);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_SelectLockedCells; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_SelectUnlockedCells;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 11); TestWriteRead_WorksheetProtection(sfOOXML, 11);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_SelectUnlockedCells; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_Objects;
begin begin
TestWriteRead_WorksheetProtection(sfOOXML, 12); TestWriteRead_WorksheetProtection(sfOOXML, 12);
end; end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_WorksheetProtection_All;
begin
TestWriteRead_WorksheetProtection(sfOOXML, 13);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_CellProtection; procedure TSpreadWriteReadProtectionTests.TestWriteRead_OOXML_CellProtection;
begin begin
TestWriteRead_CellProtection(sfOOXML); TestWriteRead_CellProtection(sfOOXML);
end; end;
{------------------------------------------------------------------------------}
{ Tests for OpenDocument file format }
{------------------------------------------------------------------------------}
procedure TSpreadWriteReadProtectionTests.TestWriteRead_ODS_WorkbookProtection_None;
begin
TestWriteRead_WorkbookProtection(sfOpenDocument, 0);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_ODS_WorkbookProtection_Struct;
begin
TestWriteRead_WorkbookProtection(sfOpenDocument, 1);
end;
{
procedure TSpreadWriteReadProtectionTests.TestWriteRead_ODS_WorkbookProtection_Win;
begin
TestWriteRead_WorkbookProtection(sfOpenDocument, 2);
end;
procedure TSpreadWriteReadProtectionTests.TestWriteRead_ODS_WorkbookProtection_StructWin;
begin
TestWriteRead_WorkbookProtection(sfOpenDocument, 3);
end;}
initialization initialization
RegisterTest(TSpreadWriteReadProtectionTests); RegisterTest(TSpreadWriteReadProtectionTests);