diff --git a/components/fpspreadsheet/source/common/xlsxml.pas b/components/fpspreadsheet/source/common/xlsxml.pas
index 2d21e5414..13ca158de 100644
--- a/components/fpspreadsheet/source/common/xlsxml.pas
+++ b/components/fpspreadsheet/source/common/xlsxml.pas
@@ -50,6 +50,7 @@ type
procedure ReadNames(ANode: TDOMNode; AWorksheet: TsBasicWorksheet);
procedure ReadNumberFormat(ANode: TDOMNode; var AFormat: TsCellFormat);
procedure ReadPageSetup(ANode: TDOMNode; AWorksheet: TsBasicWorksheet);
+ procedure ReadPrint(ANode: TDOMNode; AWorksheet: TsBasicWorksheet);
procedure ReadRow(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ARow: Integer);
procedure ReadStyle(ANode: TDOMNode);
procedure ReadStyles(ANode: TDOMNode);
@@ -82,9 +83,11 @@ type
function GetPageFooterStr(AWorksheet: TsBasicWorksheet): String;
function GetPageHeaderStr(AWorksheet: TsBasicWorksheet): String;
function GetPageMarginStr(AWorksheet: TsBasicWorksheet): String;
+ function GetPrintStr(AWorksheet: TsBasicWorksheet): String;
function GetStyleStr(AFormatIndex: Integer): String;
procedure WriteColumns(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteExcelWorkbook(AStream: TStream);
+ procedure WriteNames(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteRows(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteStyle(AStream: TStream; AIndex: Integer);
procedure WriteStyles(AStream: TStream);
@@ -142,6 +145,8 @@ const
INDENT3 = ' ';
INDENT4 = ' ';
INDENT5 = ' ';
+ NAMES_INDENT = INDENT2;
+ NAME_INDENT = INDENT3;
TABLE_INDENT = INDENT2;
ROW_INDENT = INDENT3;
COL_INDENT = INDENT3;
@@ -787,13 +792,16 @@ procedure TsSpreadExcelXMLReader.ReadNames(ANode: TDOMNode;
end;
var
- s: String;
+ sheet: TsWorksheet absolute AWorksheet;
+ s, sr: String;
nodeName: String;
sheet1, sheet2: String;
r1, c1, r2, c2: Cardinal;
flags: TsRelFlags;
p: Integer;
+ ok: Boolean;
begin
+ ok := true;
while ANode <> nil do begin
nodeName := ANode.NodeName;
if nodeName = 'NamedRange' then begin
@@ -801,9 +809,27 @@ begin
if s = 'Print_Area' then begin
//
s := GetAttrValue(ANode, 'ss:RefersTo');
- if (s <> '') and ParseCellRangeString_R1C1(s, 0, 0, sheet1, sheet2, r1, c1, r2, c2, flags) then
- TsWorksheet(AWorksheet).PageLayout.AddPrintRange(r1, c1, r2, c2);
- // to do: include sheet names here!
+ if (s <> '') then begin
+ p := pos(',', s);
+ while p > 0 do begin
+ sr := Copy(s, 1, p-1);
+ if ParseCellRangeString_R1C1(sr, 0, 0, sheet1, sheet2, r1, c1, r2, c2, flags) then
+ sheet.PageLayout.AddPrintRange(r1, c1, r2, c2)
+ else begin
+ FWorkbook.AddErrorMsg('Invalid print range.');
+ ok := false;
+ break;
+ end;
+ s := copy(s, p+1, MaxInt);
+ p := pos(',', s);
+ end;
+ if ok then begin
+ if ParseCellRangeString_R1C1(s, 0, 0, sheet1, sheet2, r1, c1, r2, c2, flags) then
+ sheet.PageLayout.AddPrintRange(r1, c1, r2, c2)
+ else
+ FWorkbook.AddErrorMsg('Invalid print range.');
+ end;
+ end;
end else
if s = 'Print_Titles' then begin
//
@@ -944,6 +970,63 @@ begin
end;
end;
+{@@ ----------------------------------------------------------------------------
+ Reads the "WorksheetOptions/Print" node
+-------------------------------------------------------------------------------}
+procedure TsSpreadExcelXMLReader.ReadPrint(ANode: TDOMNode;
+ AWorksheet: TsBasicWorksheet);
+var
+ sheet: TsWorksheet absolute AWorksheet;
+ nodeName: String;
+ s: String;
+ n: Integer;
+ x: Double;
+begin
+ while ANode <> nil do begin
+ nodeName := ANode.NodeName;
+ if nodeName = 'PaperSizeIndex' then begin
+ s := ANode.TextContent;
+ if (s <> '') and TryStrToInt(s, n) and (n < Length(PAPER_SIZES)) then begin
+ sheet.PageLayout.PageWidth := PAPER_SIZES[n, 0];
+ sheet.PageLayout.pageHeight := PAPER_SIZES[n, 1];
+ end;
+ end
+ else if nodeName = 'FitHeight' then begin
+ s := ANode.TextContent;
+ if (s <> '') and TryStrToInt(s, n) then
+ sheet.PageLayout.FitHeightToPages := n;
+ end
+ else if nodeName = 'FitWidth' then begin
+ s := ANode.TextContent;
+ if (s <> '') and TryStrToInt(s, n) then
+ sheet.PageLayout.FitWidthToPages := n;
+ end
+ else if nodeName = 'Scale' then begin
+ s := ANode.TextContent;
+ if (s <> '') and TryStrToInt(s, n) then
+ sheet.PageLayout.ScalingFactor := n;
+ end
+ else if nodeName = 'Gridlines' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintGridLines]
+ else if nodeName = 'BlackAndWhite' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poMonochrome]
+ else if nodeName = 'DraftQuality' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poDraftQuality]
+ else if nodeName = 'LeftToRight' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintPagesByRows]
+ else if nodeName = 'RowColHeadings' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintHeaders]
+ else if nodeName = 'CommentsLayout' then begin
+ s := ANode.TextContent;
+ if s = 'SheetEnd' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poCommentsAtEnd]
+ else if s = 'InPlace' then
+ sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintCellComments];
+ end;
+ ANode := ANode.NextSibling;
+ end;
+end;
+
{@@ ----------------------------------------------------------------------------
Reads a "Worksheet/Table/Row" node
-------------------------------------------------------------------------------}
@@ -1195,49 +1278,7 @@ begin
end else
if nodeName = 'Print' then begin
node := ANode.FirstChild;
- while node <> nil do begin
- nodeName := node.NodeName;
- if nodeName = 'PaperSizeIndex' then begin
- s := node.TextContent;
- if (s <> '') and TryStrToInt(s, n) and (n < Length(PAPER_SIZES)) then begin
- sheet.PageLayout.PageWidth := PAPER_SIZES[n, 0];
- sheet.PageLayout.pageHeight := PAPER_SIZES[n, 1];
- end;
- end
- else if nodeName = 'FitHeight' then begin
- s := node.TextContent;
- if (s <> '') and TryStrToInt(s, n) then
- sheet.PageLayout.FitHeightToPages := n;
- end
- else if nodeName = 'FitWidth' then begin
- s := node.TextContent;
- if (s <> '') and TryStrToInt(s, n) then
- sheet.PageLayout.FitWidthToPages := n;
- end
- else if nodeName = 'Scale' then begin
- s := node.TextContent;
- if (s <> '') and TryStrToInt(s, n) then
- sheet.PageLayout.ScalingFactor := n;
- end
- else if nodeName = 'Gridlines' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintGridLines]
- else if nodeName = 'BlackAndWhite' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poMonochrome]
- else if nodeName = 'DraftQuality' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poDraftQuality]
- else if nodeName = 'LeftToRight' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintPagesByRows]
- else if nodeName = 'RowColHeadings' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintHeaders]
- else if nodeName = 'CommentsLayout' then begin
- s := node.TextContent;
- if s = 'SheetEnd' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poCommentsAtEnd]
- else if s = 'InPlace' then
- sheet.PageLayout.Options := sheet.PageLayout.Options + [poPrintCellComments];
- end;
- node := node.NextSibling;
- end;
+ ReadPrint(ANode.FirstChild, AWorksheet);
end else
if nodeName = 'Selected' then
TsWorkbook(FWorkbook).ActiveWorksheet := sheet
@@ -1615,6 +1656,49 @@ begin
Result := '';
end;
+{ Todo: When can the "Print" node be skipped? }
+function TsSpreadExcelXMLWriter.GetPrintStr(AWorksheet: TsBasicWorksheet): String;
+var
+ sheet: TsWorksheet absolute AWorksheet;
+ i, pgSizeIdx: Integer;
+ scalestr: String;
+begin
+ Result := '';
+ pgSizeIdx := -1;
+ for i:=0 to High(PAPER_SIZES) do
+ if (SameValue(PAPER_SIZES[i,0], sheet.PageLayout.PageHeight) and
+ SameValue(PAPER_SIZES[i,1], sheet.PageLayout.PageWidth))
+ or (SameValue(PAPER_SIZES[i,1], sheet.PageLayout.PageHeight) and
+ SameValue(PAPER_SIZES[i,0], sheet.PageLayout.PageWidth))
+ then begin
+ pgSizeIdx := i;
+ break;
+ end;
+
+ if pgSizeidx = -1 then
+ exit;
+
+ // Scaling factor
+ if sheet.PageLayout.ScalingFactor <> 100 then
+ scaleStr := INDENT4 + '' + IntToStr(sheet.PageLayout.ScalingFactor) + '' + LF
+ else
+ scaleStr := '';
+
+ Result :=
+ INDENT4 + '' + LF +
+ INDENT4 + '' + IntToStr(pgSizeIdx) + '' + LF +
+ scaleStr +
+ INDENT4 + '0';
+
+ if sheet.PageLayout.FitHeightToPages > 1 then
+ Result := Result + LF + INDENT4 +
+ '' + IntToStr(sheet.PageLayout.FitHeightToPages) + '';
+
+ if sheet.PageLayout.FitWidthToPages > 1 then
+ Result := result + LF + INDENT4 +
+ '' + IntToStr(sheet.PageLayout.FitWidthToPages) + '';
+end;
+
function TsSpreadExcelXMLWriter.GetStyleStr(AFormatIndex: Integer): String;
begin
Result := '';
@@ -1890,6 +1974,70 @@ begin
]));
end;
+procedure TsSpreadExcelXMLWriter.WriteNames(AStream: TStream;
+ AWorksheet: TsBasicWorksheet);
+var
+ sheet: TsWorksheet absolute AWorksheet;
+ print_titles_str: string = '';
+ print_range_str: String = '';
+ s: String;
+ rng: TsCellRange;
+ i: Integer;
+begin
+ with sheet.PageLayout do begin
+
+ // Print ranges --> Name "Print_Area"
+ for i:=0 to NumPrintRanges-1 do begin
+ rng := GetPrintRange(i);
+ s := GetCellRangeString_R1C1(sheet.Name, sheet.Name, rng.Row1, rng.Col1, rng.Row2, rng.Col2, []);
+ if print_range_str = '' then
+ print_range_str := s
+ else
+ print_range_str := print_range_str + ',' + s;
+ end;
+ if print_range_str <> '' then
+ print_range_str := NAME_INDENT +
+ '' + LF;
+
+ // Repeated columns --> Name "Print_Titles"
+ if (RepeatedCols.FirstIndex <> UNASSIGNED_ROW_COL_INDEX) and
+ (RepeatedCols.LastIndex <> UNASSIGNED_ROW_COL_INDEX)
+ then begin
+ s := 'C' + IntToStr(RepeatedCols.FirstIndex + 1);
+ if RepeatedCols.FirstIndex <> RepeatedCols.LastIndex then
+ s := s + ':C' + IntToStr(RepeatedCols.LastIndex + 1);
+ s := sheet.Name + '!' + s;
+ print_titles_str := s;
+ end;
+
+ // Repeated rows --> Name "Print_Titles"
+ if (RepeatedRows.FirstIndex <> UNASSIGNED_ROW_COL_INDEX) and
+ (RepeatedRows.LastIndex <> UNASSIGNED_ROW_COL_INDEX)
+ then begin
+ s := 'R' + IntToStr(RepeatedRows.FirstIndex + 1);
+ if RepeatedRows.FirstIndex <> RepeatedRows.LastIndex then
+ s := s + ':R' + IntToStr(RepeatedRows.LastIndex + 1);
+ s := sheet.Name + '!' + s;
+ if print_titles_str = '' then
+ print_titles_str := s
+ else
+ print_titles_str := print_titles_str + ',' + s;
+ end;
+ if print_titles_str <> '' then
+ print_titles_str := NAME_INDENT +
+ '' + LF;
+ end;
+
+ if (print_range_str = '') and (print_titles_str = '') then
+ exit;
+
+ AppendToStream(AStream, NAMES_INDENT +
+ '' + LF +
+ print_titles_str + NAMES_INDENT +
+ print_range_str + NAMES_INDENT +
+ '' + LF);
+end;
+
procedure TsSpreadExcelXMLWriter.WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: double; ACell: PCell);
begin
@@ -2229,7 +2377,8 @@ begin
FWorksheet := AWorksheet;
if FWorksheet.IsProtected then
- protectedStr := ' ss:Protected="1"' else
+ protectedStr := ' ss:Protected="1"'
+ else
protectedStr := '';
AppendToStream(AStream, Format(
@@ -2237,6 +2386,7 @@ begin
UTF8TextToXMLText(AWorksheet.Name),
protectedStr
]) );
+ WriteNames(AStream, AWorksheet);
WriteTable(AStream, AWorksheet);
WriteWorksheetOptions(AStream, AWorksheet);
AppendToStream(AStream,
@@ -2247,7 +2397,6 @@ end;
procedure TsSpreadExcelXMLWriter.WriteWorksheetOptions(AStream: TStream;
AWorksheet: TsBasicWorksheet);
var
- i: Integer;
footerStr, headerStr: String;
hideGridStr: String;
hideHeadersStr: String;
@@ -2258,7 +2407,8 @@ var
protectStr: String;
visibleStr: String;
printStr: String;
- scaleStr: String;
+ fitToPageStr: String;
+ enableSelectionStr: String;
sheet: TsWorksheet absolute AWorksheet;
begin
// Orientation, some PageLayout.Options
@@ -2294,26 +2444,14 @@ begin
else
selectedStr := '';
- // Scaling factor
- if sheet.PageLayout.ScalingFactor <> 100 then
- scaleStr := '' + IntToStr(sheet.PageLayout.ScalingFactor) + '' + LF + INDENT4
+ // FitToPage node
+ if poFitPages in sheet.PageLayout.Options then
+ fitToPageStr := INDENT3 + '' + LF
else
- scaleStr := '';
+ fitToPageStr := '';
+
// Print node
- printStr := '';
- for i:=0 to High(PAPER_SIZES) do
- if (SameValue(PAPER_SIZES[i,0], sheet.PageLayout.PageHeight) and
- SameValue(PAPER_SIZES[i,1], sheet.PageLayout.PageWidth))
- or (SameValue(PAPER_SIZES[i,1], sheet.PageLayout.PageHeight) and
- SameValue(PAPER_SIZES[i,0], sheet.PageLayout.PageWidth))
- then begin
- printStr := INDENT4 +
- '' + LF + INDENT4 +
- '' + IntToStr(i) + '' + LF + INDENT4 +
- scaleStr +
- '0';
- break;
- end;
+ printStr := GetPrintStr(AWorksheet);
// Visible
if (soHidden in AWorksheet.Options) then
@@ -2327,10 +2465,21 @@ begin
// Protection
protectStr := Format(INDENT3 + '%s' + LF +
INDENT3 + '%s' + LF, [
- StrUtils.IfThen(AWorksheet.IsProtected and (spObjects in AWorksheet.Protection), 'True', 'False'),
+ StrUtils.IfThen(spObjects in AWorksheet.Protection, 'True', 'False'),
StrUtils.IfThen(AWorksheet.IsProtected {and [spScenarios in AWorksheet.Protection])}, 'True', 'False')
]);
+ // Enable selection
+ enableSelectionStr := '';
+ if (sheet.Protection * [spSelectLockedCells, spSelectUnlockedCells] <> []) then begin
+ enableSelectionStr := INDENT3 + '' + LF;
+ if spSelectUnlockedCells in sheet.Protection then
+ enableSelectionStr := enableSelectionStr + INDENT4 + '' + LF;
+ if (sheet.Protection * [spSelectLockedCells, spSelectUnlockedCells] = [spSelectLockedCells]) then
+ enableSelectionStr := enableSelectionStr + INDENT4 + '' + LF;
+ enableSelectionStr := INDENT3 + '' + LF;
+ end;
+
// todo - Several protection options
// Put it all together...
@@ -2341,12 +2490,23 @@ begin
headerStr +
footerStr +
marginStr + INDENT3 +
- '' + LF + INDENT3 +
+ '' + LF +
+ fitToPageStr + INDENT3 +
'' + LF +
printStr + LF + INDENT3 +
'' + LF +
visibleStr +
selectedStr +
+ IfThen(not (spFormatCells in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spFormatColumns in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spFormatRows in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spDeleteColumns in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spDeleteRows in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spInsertColumns in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spInsertHyperlinks in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spInsertRows in sheet.Protection), INDENT4 + '' + LF) +
+ IfThen(not (spSort in sheet.Protection), INDENT4 + '' + LF) +
+ enableSelectionStr +
protectStr +
frozenStr +
hideGridStr +
diff --git a/components/fpspreadsheet/tests/protectiontests.pas b/components/fpspreadsheet/tests/protectiontests.pas
index 45ab175ad..e7ec70d9f 100644
--- a/components/fpspreadsheet/tests/protectiontests.pas
+++ b/components/fpspreadsheet/tests/protectiontests.pas
@@ -116,10 +116,8 @@ type
procedure TestWriteRead_XML_WorksheetProtection_SelectLockedCells;
procedure TestWriteRead_XML_WorksheetProtection_SelectUnlockedCells;
procedure TestWriteRead_XML_WorksheetProtection_Objects;
-
procedure TestWriteRead_XML_CellProtection;
-
- procedure TestWriteRead_XML_Passwords;
+ //procedure TestWriteRead_XML_Passwords; // not allowed
{ ODS protection tests }
procedure TestWriteRead_ODS_WorkbookProtection_None;
@@ -237,7 +235,7 @@ begin
9: Exclude(expected, spSort);
10: Exclude(expected, spSelectLockedCells);
11: Exclude(expected, spSelectUnlockedCells);
- 12: Exclude(expected, spObjects);
+ 12: Include(expected, spObjects);
end;
{
case ACondition of
@@ -785,11 +783,6 @@ begin
TestWriteRead_CellProtection(sfExcelXML);
end;
-procedure TSpreadWriteReadProtectionTests.TestWriteRead_XML_Passwords;
-begin
- TestWriteRead_Passwords(sfExcelXML);
-end;
-
{------------------------------------------------------------------------------}
{ Tests for OpenDocument file format }