fpspreadsheet: Add writing of conditional formats to ODS (only cfcEqual condition, so far).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7504 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2020-06-30 10:46:07 +00:00
parent b80085c78d
commit aff774ebc9
2 changed files with 457 additions and 155 deletions

View File

@ -2,7 +2,8 @@ program demo_conditional_formatting;
uses uses
sysUtils, sysUtils,
fpsTypes, fpsUtils, fpspreadsheet, xlsxooxml, fpsconditionalformat; fpsTypes, fpsUtils, fpspreadsheet, fpsConditionalFormat,
xlsxooxml, fpsOpenDocument;
var var
wb: TsWorkbook; wb: TsWorkbook;
@ -59,7 +60,7 @@ begin
fmtIdx := wb.AddCellFormat(fmt); fmtIdx := wb.AddCellFormat(fmt);
// Write conditional format // Write conditional format
sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcEqual, 5, fmtIdx); sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcEqual, 5, fmtIdx);
(*
// conditional format #2: equal to text constant // conditional format #2: equal to text constant
inc(row); inc(row);
sh.WriteText(row, 0, 'equal to text "abc"'); sh.WriteText(row, 0, 'equal to text "abc"');
@ -262,96 +263,19 @@ begin
fmt.SetBackgroundColor(scRed); fmt.SetBackgroundColor(scRed);
fmtIdx := wb.AddCellFormat(fmt); fmtIdx := wb.AddCellFormat(fmt);
sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotContainsErrors, fmtIdx); sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotContainsErrors, fmtIdx);
*)
(* WriteLn('row = ', row);
WriteLn('wb.GetNumcellFormats = ', wb.GetNumCellFormats);
sh.Wri WriteLn('wb.GetNumConditionalFormats = ', wb.GetNumConditionalFormats);
'
{ ------ 1st conditional format : cfcEqual ------------------------------- }
sh.WriteNumber(0, 0, 1.0);
sh.WriteNumber(1, 0, 2.0);
sh.WriteNumber(2, 0, 3.0);
sh.WriteNumber(3, 0, 4.0);
sh.WriteNumber(4, 0, 5.0);
// Prepare the format record
InitFormatRecord(fmt);
// ... set the background color
fmt.SetBackgroundColor(scYellow);
// ... set the borders
fmt.SetBorders([cbNorth, cbEast, cbWest], scBlack, lsThin);
fmt.SetBorders([cbSouth], scRed, lsThick);
// ... set the font (bold) ---- NOT SUPPORTED AT THE MOMENT FOR WRITING TO XLSX...
font := wb.CloneFont(0);
font.Style := [fssBold, fssItalic];
font.Color := scRed;
fmt.SetFont(wb.AddFont(font));
// Add format record to format list
fmtIdx := wb.AddCellFormat(fmt);
// Use the format as conditional format of A1:A6 when cells are equal to 3.
sh.WriteConditionalCellFormat(Range(0, 0, 5, 0), cfcEqual, 3.0, fmtIdx);
{ ------- 2nd conditional format : cfcBelowEqualAverage ------------------ }
sh.WriteNumber(0, 2, 10.0);
sh.WriteNumber(1, 2, 20.0);
sh.WriteNumber(2, 2, 15.0);
sh.WriteNumber(3, 2, 11.0);
sh.WriteNumber(4, 2, 19.0);
InitFormatRecord(fmt);
fmt.SetBackgroundColor(scRed);
fmtIdx := wb.AddCellFormat(fmt);
sh.WriteConditionalCellFormat(Range(0, 2, 4, 2), cfcBelowEqualAverage, fmtIdx);
{ ------- 3rd and 4th conditional formats : beginWith, containsText ------ }
sh.WriteText(0, 4, 'abc');
sh.WriteText(1, 4, 'def');
sh.WriteText(2, 4, 'bac');
sh.WriteText(3, 4, 'dbc');
sh.WriteText(4, 4, 'acb');
sh.WriteText(5, 4, 'aca');
InitFormatRecord(fmt);
fmt.SetBackgroundColor($DEF1F4);
fmtIdx := wb.AddCellFormat(fmt);
sh.WriteConditionalCellFormat(Range(0, 4, 5, 4), cfcBeginsWith, 'a', fmtIdx);
fmt.SetBackgroundColor($D08330);
fmtIdx := wb.AddCellFormat(fmt);
sh.WriteConditionalCellFormat(Range(0, 4, 5, 4), cfcContainsText, 'bc', fmtIdx);
{ ------ 5th conditional format: containsErrors -------------------------- }
sh.WriteFormula(0, 6, '=1.0/0.0');
sh.WriteFormula(1, 6, '=1.0/1.0');
sh.WriteFormula(2, 6, '=1.0/2.0');
InitFormatRecord(fmt);
fmt.SetBackgroundColor(scGreen);
fmtIdx := wb.AddCellFormat(fmt);
sh.WriteConditionalCellFormat(Range(0, 6, 5, 6), cfcNotContainsErrors, fmtIdx);
// Condition for ContainsErrors after NoContainsErrors to get higher priority
fmt.SetBackgroundColor(scRed);
fmtIdx := wb.AddCellFormat(fmt);
sh.WriteConditionalCellFormat(Range(0, 0, 100, 100), cfcContainsErrors, fmtIdx);
{ ------ 6th conditional format: unique/duplicate values ----------------- }
sh.WriteNumber(0, 1, 1.0);
sh.WriteNumber(1, 1, 99.0);
InitFormatRecord(fmt);
fmt.SetBackgroundColor(scSilver);
sh.WriteConditionalCellFormat(Range(0, 0, 1, 1), cfcUnique, wb.AddCellFormat(fmt));
fmt.SetBackgroundColor(scGreen);
sh.WriteConditionalCellFormat(Range(0, 0, 1, 1), cfcDuplicate, wb.AddCellFormat(fmt));
*)
{ ------ Save workbook to file-------------------------------------------- } { ------ Save workbook to file-------------------------------------------- }
wb.WriteToFile('test.xlsx', true); wb.WriteToFile('test.xlsx', true);
wb.WriteToFile('test.ods', true);
finally finally
wb.Free; wb.Free;
end; end;
ReadLn;
end. end.

View File

@ -193,9 +193,13 @@ type
procedure WriteAutomaticStyles(AStream: TStream); procedure WriteAutomaticStyles(AStream: TStream);
procedure WriteCellRow(AStream: TStream; ASheet: TsBasicWorksheet; procedure WriteCellRow(AStream: TStream; ASheet: TsBasicWorksheet;
ARowIndex, ALastColIndex: Integer); ARowIndex, ALastColIndex: Integer);
procedure WriteCellStyle(AStream: TStream; AFormatIndex, AConditionalFormatIndex: integer);
procedure WriteCellStyles(AStream: TStream); procedure WriteCellStyles(AStream: TStream);
procedure WriteColStyles(AStream: TStream); procedure WriteColStyles(AStream: TStream);
procedure WriteColumns(AStream: TStream; ASheet: TsBasicWorksheet); procedure WriteColumns(AStream: TStream; ASheet: TsBasicWorksheet);
procedure WriteConditionalFormats(AStream: TStream; ASheet: TsBasicWorksheet);
procedure WriteConditionalStyle(AStream: TStream; AStyleName: String; const AFormat: TsCellFormat);
procedure WriteConditionalStyles(AStream: TStream);
procedure WriteEmptyRow(AStream: TStream; ASheet: TsBasicWorksheet; procedure WriteEmptyRow(AStream: TStream; ASheet: TsBasicWorksheet;
ARowIndex, AFirstColIndex, ALastColIndex, ALastRowIndex: Integer; ARowIndex, AFirstColIndex, ALastColIndex, ALastRowIndex: Integer;
out ARowsRepeated: Integer); out ARowsRepeated: Integer);
@ -216,6 +220,7 @@ type
function WriteBorderStyleXMLAsString(const AFormat: TsCellFormat): String; function WriteBorderStyleXMLAsString(const AFormat: TsCellFormat): String;
function WriteCellProtectionStyleXMLAsString(const AFormat: TsCellFormat): String; function WriteCellProtectionStyleXMLAsString(const AFormat: TsCellFormat): String;
function WriteCommentXMLAsString(AComment: String): String; function WriteCommentXMLAsString(AComment: String): String;
function WriteConditionalStyleXMLAsString(AFormatIndex: Integer): String;
function WriteDefaultFontXMLAsString: String; function WriteDefaultFontXMLAsString: String;
function WriteDefaultGraphicStyleXMLAsString: String; overload; function WriteDefaultGraphicStyleXMLAsString: String; overload;
function WriteDocumentProtectionXMLAsString: String; function WriteDocumentProtectionXMLAsString: String;
@ -246,6 +251,7 @@ type
out AHeader, AFooter: String); out AHeader, AFooter: String);
procedure GetHeaderFooterImagePosStr(APagelayout: TsPageLayout; procedure GetHeaderFooterImagePosStr(APagelayout: TsPageLayout;
out AHeader, AFooter: String); out AHeader, AFooter: String);
function GetStyleName(ACell: PCell): String;
{ {
procedure GetRowStyleAndHeight(ASheet: TsBasicWorksheet; ARowIndex: Integer; procedure GetRowStyleAndHeight(ASheet: TsBasicWorksheet; ARowIndex: Integer;
out AStyleName: String; out AHeight: Single); out AStyleName: String; out AHeight: Single);
@ -308,7 +314,7 @@ uses
fpsPatches, fpsPatches,
{$ENDIF} {$ENDIF}
fpsStrings, fpsStreams, fpsCrypto, fpsClasses, fpspreadsheet, fpsStrings, fpsStreams, fpsCrypto, fpsClasses, fpspreadsheet,
fpsExprParser, fpsImages; fpsExprParser, fpsImages, fpsConditionalFormat;
const const
{ OpenDocument general XML constants } { OpenDocument general XML constants }
@ -4808,6 +4814,24 @@ begin
AFooter := GetPosStr(ftrTags); AFooter := GetPosStr(ftrTags);
end; end;
function TsSpreadOpenDocWriter.GetStyleName(ACell: PCell): String;
var
fmt: TsCellFormat;
ncf: Integer;
begin
ncf := Length(ACell^.ConditionalFormatIndex);
if ncf > 0 then
Result := Format('ce%d_%d', [ACell^.FormatIndex, ACell^.ConditionalFormatIndex[ncf-1]])
else
begin
fmt := (FWorkbook as TsWorkbook).GetCellFormat(ACell^.FormatIndex);
if fmt.UsedFormattingFields <> [] then
Result := 'ce' + IntToStr(ACell^.FormatIndex)
else
Result := '';
end;
end;
procedure TsSpreadOpenDocWriter.InternalWriteToStream(AStream: TStream); procedure TsSpreadOpenDocWriter.InternalWriteToStream(AStream: TStream);
var var
FZip: TZipper; FZip: TZipper;
@ -5244,6 +5268,7 @@ begin
'</office:document-settings>'); '</office:document-settings>');
end; end;
{ Writes the file "styles.xml" }
procedure TsSpreadOpenDocWriter.WriteStyles; procedure TsSpreadOpenDocWriter.WriteStyles;
begin begin
AppendToStream(FSStyles, AppendToStream(FSStyles,
@ -5272,6 +5297,9 @@ begin
'<style:style style:name="Default" style:family="table-cell">', '<style:style style:name="Default" style:family="table-cell">',
WriteDefaultFontXMLAsString, WriteDefaultFontXMLAsString,
'</style:style>'); '</style:style>');
WriteConditionalStyles(FSStyles);
if (FWorkbook as TsWorkbook).HasEmbeddedSheetImages then if (FWorkbook as TsWorkbook).HasEmbeddedSheetImages then
AppendToStream(FSStyles, AppendToStream(FSStyles,
'<style:default-style style:family="graphic">', '<style:default-style style:family="graphic">',
@ -5429,6 +5457,9 @@ begin
else else
WriteRowsAndCells(AStream, FWorksheet); WriteRowsAndCells(AStream, FWorksheet);
// Conditional formats
WriteConditionalFormats(AStream, FWorksheet);
// named expressions, i.e. print range, repeated cols/rows // named expressions, i.e. print range, repeated cols/rows
WriteNamedExpressions(AStream, FWorksheet); WriteNamedExpressions(AStream, FWorksheet);
@ -5437,78 +5468,165 @@ begin
'</table:table>'); '</table:table>');
end; end;
procedure TsSpreadOpenDocWriter.WriteCellStyle(AStream: TStream;
AFormatIndex, AConditionalFormatIndex: integer);
var
book: TsWorkbook;
fmt: TsCellFormat;
nfs: String;
nfParams: TsNumFormatParams;
nfidx: Integer;
s: String;
p: Integer;
j: Integer;
stylename: String;
cf: TsConditionalFormat;
isConditionalFormat: Boolean;
begin
book := TsWorkbook(FWorkbook);
isConditionalFormat := AConditionalFormatIndex > -1;
// The style name will be 'ce' plus format index in the workbook's CellFormats
// list.
styleName := 'ce' + IntToStr(AFormatIndex);
// In case of a conditional format the index in the worksheet's
// ConditionalFormatList will be added after an underscore character.
if isConditionalFormat then
styleName := styleName + '_' + IntToStr(AConditionalFormatIndex);
fmt := book.GetCellFormat(AFormatIndex);
nfs := '';
nfidx := fmt.NumberFormatIndex;
if nfidx <> -1 then
begin
nfParams := book.GetNumberFormat(nfidx);
if nfParams <> nil then
begin
nfs := nfParams.NumFormatStr;
for j:=0 to NumFormatList.Count-1 do
begin
s := NumFormatList[j];
p := pos(':', s);
if SameText(Copy(s, p+1, Length(s)), nfs) then
begin
nfs := Format('style:data-style-name="%s"', [copy(s, 1, p-1)]);
break;
end;
p := 0;
end;
if p = 0 then // not found
nfs := '';
end;
end;
// Start and name
AppendToStream(AStream,
'<style:style style:name="' + styleName + '" style:family="table-cell" ' +
'style:parent-style-name="Default" '+ nfs + '>');
// style:text-properties
// - font
s := WriteFontStyleXMLAsString(fmt);
if s <> '' then
AppendToStream(AStream,
'<style:text-properties '+ s + '/>');
// - border, background, wordwrap, text rotation, vertical alignment
s := WriteBorderStyleXMLAsString(fmt) +
WriteBackgroundColorStyleXMLAsString(fmt) +
WriteWordwrapStyleXMLAsString(fmt) +
WriteTextRotationStyleXMLAsString(fmt) +
WriteVertAlignmentStyleXMLAsString(fmt);
if not isConditionalFormat then
s := s + WriteCellProtectionStyleXMLAsString(fmt);
if s <> '' then
AppendToStream(AStream,
'<style:table-cell-properties ' + s + '/>');
// style:paragraph-properties
// - hor alignment, bidi
s := WriteHorAlignmentStyleXMLAsString(fmt) +
WriteBiDiModeStyleXMLAsString(fmt);
if s <> '' then
AppendToStream(AStream,
'<style:paragraph-properties ' + s + '/>');
if isConditionalFormat then
begin
s := WriteConditionalStyleXMLAsString(AConditionalFormatIndex);
if s <> '' then
AppendToStream(AStream, s);
end;
// End
AppendToStream(AStream,
'</style:style>');
end;
{ Writes the cell styles ("ce0", "ce1", ...). Directly maps to the CellFormats { Writes the cell styles ("ce0", "ce1", ...). Directly maps to the CellFormats
list of the workbook. "ce0" is the default format } list of the workbook. "ce0" is the default format }
procedure TsSpreadOpenDocWriter.WriteCellStyles(AStream: TStream); procedure TsSpreadOpenDocWriter.WriteCellStyles(AStream: TStream);
var var
i, j, p: Integer; book: TsWorkbook;
cf: TsConditionalFormat;
cf_sheet: TsWorksheet;
cf_range: TsCellRange;
cf_rule: TsCFCellRule;
ncf: Integer;
i, j, k, p: Integer;
cell: PCell;
r, c: Cardinal;
L: TStrings;
s: String; s: String;
nfidx: Integer; fmtIndex, cfIndex: Integer;
nfs: String;
fmt: TsCellFormat;
nfParams: TsNumFormatParams;
begin begin
for i := 0 to (FWorkbook as TsWorkbook).GetNumCellFormats - 1 do book := TsWorkbook(FWorkbook);
begin
fmt := TsWorkbook(FWorkbook).GetCellFormat(i); // Write fixed formats only
nfs := ''; for i := 0 to book.GetNumCellFormats - 1 do
nfidx := fmt.NumberFormatIndex; WriteCellStyle(AStream, i, -1);
if nfidx <> -1 then
// Conditional formats contain the fixed formats plus the condition params
// To avoid duplicate style entries in the file we first collect all style
// names in a list
ncf := book.GetNumConditionalFormats;
if ncf = 0 then
exit;
L := TStringList.Create;
try
for i := 0 to ncf - 1 do
begin begin
nfParams := TsWorkbook(FWorkbook).GetNumberFormat(nfidx); cf := book.GetConditionalFormat(i);
if nfParams <> nil then cf_sheet := TsWorksheet(cf.Worksheet);
begin cf_range := cf.Cellrange;
nfs := nfParams.NumFormatStr; for r := cf_range.Row1 to cf_range.Row2 do
for j:=0 to NumFormatList.Count-1 do for c := cf_range.Col1 to cf_range.Col2 do
begin begin
s := NumFormatList[j]; cell := cf_sheet.FindCell(r, c);
p := pos(':', s); if Assigned(cell) and
if SameText(Copy(s, p+1, Length(s)), nfs) then (cell^.ConditionalFormatIndex[High(cell^.ConditionalFormatIndex)] = i)
begin then begin
nfs := Format('style:data-style-name="%s"', [copy(s, 1, p-1)]); s := Format('ce%d_%d', [cell^.FormatIndex, i]);
break; if L.IndexOf(s) = -1 then
L.Add(s);
end; end;
p := 0;
end; end;
if p = 0 then // not found
nfs := '';
end;
end; end;
// Start and name // Now write the combined styles to the stream. The styles can be identified
AppendToStream(AStream, // from the style name in the string list.
'<style:style style:name="ce' + IntToStr(i) + '" style:family="table-cell" ' + for i := 0 to L.Count-1 do begin
'style:parent-style-name="Default" '+ nfs + '>'); s := L[i];
p := pos('_', L[i]);
// style:text-properties fmtIndex := StrToInt(Copy(L[i], 3, p-3));
// - font cfIndex := StrToInt(Copy(L[i], p+1, MaxInt));
s := WriteFontStyleXMLAsString(fmt); WriteCellStyle(AStream, fmtIndex, cfIndex);
if s <> '' then end;
AppendToStream(AStream, finally
'<style:text-properties '+ s + '/>'); L.Free;
// - border, background, wordwrap, text rotation, vertical alignment
s := WriteBorderStyleXMLAsString(fmt) +
WriteBackgroundColorStyleXMLAsString(fmt) +
WriteWordwrapStyleXMLAsString(fmt) +
WriteTextRotationStyleXMLAsString(fmt) +
WriteVertAlignmentStyleXMLAsString(fmt) +
WriteCellProtectionStyleXMLAsString(fmt);
if s <> '' then
AppendToStream(AStream,
'<style:table-cell-properties ' + s + '/>');
// style:paragraph-properties
// - hor alignment, bidi
s := WriteHorAlignmentStyleXMLAsString(fmt) +
WriteBiDiModeStyleXMLAsString(fmt);
if s <> '' then
AppendToStream(AStream,
'<style:paragraph-properties ' + s + '/>');
// End
AppendToStream(AStream,
'</style:style>');
end; end;
end; end;
@ -5708,6 +5826,171 @@ begin
Result := Result + '</office:annotation>'; Result := Result + '</office:annotation>';
end; end;
{@@ ----------------------------------------------------------------------------
Writes the "calcext:conditional-formats" node after the table block
in "contents.xml". This is the third part needed for conditional formatting.
The other part are implemented in
#1 WriteConditionalStyles/WriteConditionalStyle
#2 WriteCellStyle
-------------------------------------------------------------------------------}
procedure TsSpreadOpenDocWriter.WriteConditionalFormats(AStream: TStream;
ASheet: TsBasicWorksheet);
{<calcext:conditional-formats>
<calcext:conditional-format calcext:target-range-address="Tabelle1.B4:Tabelle1.J4">
<calcext:condition calcext:apply-style-name="cf" calcext:value="=5" calcext:base-cell-address="Tabelle1.B4" />
</calcext:conditional-format>
</calcext:conditional-formats> }
var
book: TsWorkbook;
ncf: Integer;
cf: TsConditionalFormat;
cf_range: TsCellRange;
cf_styleName: String;
cf_cellRule: TsCFCellRule;
i, j: Integer;
sheet: TsWorksheet;
rangeStr: String;
firstCellStr: string;
value1Str, value2Str: String;
s: String;
begin
book := TsWorkbook(FWorkbook);
sheet := TsWorksheet(ASheet);
ncf := book.GetNumConditionalFormats;
for i := 0 to ncf-1 do begin
cf := book.GetConditionalFormat(i);
if cf.Worksheet <> ASheet then
continue;
cf_styleName := 'cf' + IntToStr(i);
cf_range := cf.CellRange;
firstCellStr := sheet.Name + '.' + GetCellString(cf_range.Row1, cf_range.Col1);
rangeStr := firstCellStr + ':' + sheet.Name + '.' + GetCellString(cf_range.Row2, cf_range.Col2);
if cf.Rules[cf.RulesCount-1] is TsCFCellRule then
begin
cf_cellRule := TsCFCellRule(cf.Rules[cf.RulesCount-1]);
case cf_cellRule.Condition of
cfcEqual:
if VarIsStr(cf_cellRule.Operand1) then
value1Str := '=' + UTF8TextToXMLText(SafeQuoteStr(cf_cellrule.Operand1))
else
value1Str := '=' + VarToStr(cf_cellRule.Operand1);
else
Continue;
end;
s := Format(
'<calcext:conditional-formats>' +
'<calcext:conditional-format calcext:target-range-address="%s">' +
'<calcext:condition calcext:apply-style-name="%s" calcext:value="%s" calcext:base-cell-address="%s" />' +
'</calcext:conditional-format>' +
'</calcext:conditional-formats>', [
rangeStr, cf_stylename, value1Str, firstCellStr
]);
AppendToStream(AStream, s);
end;
(*
for j := 0 to cf.RulesCount-1 do
begin
range := cf.CellRange;
firstCellStr := sheet.Name + '.' + GetCellString(range.Row1, range.Col1);
rangeStr := firstCellStr + ':' + sheet.Name + '.' + GetCellString(range.Row2, range.Col2);
if cf.Rules[j] is TsCFCellRule then
begin
cellRule := TsCFCellRule(cf.Rules[j]);
cf_styleName := Format('cf%d_%d', [sheet.Index, cellRule.FormatIndex]);
case cellRule.Condition of
cfcEqual: value1Str := '=' + VarToStr(cellRule.Operand1);
else Continue;
end;
s := Format(
'<calcext:conditional-formats>' +
'<calcext:conditional-format calcext:target-range-address="%s">' +
'<calcext:condition calcext:apply-style-name="%s" calcext:value="%s" calcext:base-cell-address="%s" />' +
'</calcext:conditional-format>' +
'</calcext:conditional-formats>', [
rangeStr, cf_stylename, value1Str, firstCellStr
]);
AppendToStream(AStream, s);
end;
end;
*)
end;
end;
{ Writes the conditional format part of a style to "styles.xml". }
procedure TsSpreadopenDocWriter.WriteConditionalStyle(AStream: TStream;
AStyleName: String; const AFormat: TsCellFormat);
begin
AppendToStream(AStream, Format(
'<style:style style:name="%s" style:family="table-cell" style:parent-style-name="Default">',
[AStyleName]));
AppendToStream(AStream, Format(
'<style:table-cell-properties %s%s />', [
WriteBackgroundColorStyleXMLAsString(AFormat),
WriteBorderStyleXMLAsString(AFormat)
// To do: add the remaining style elements
]));
AppendToStream(AStream,
'</style:style>');
end;
{@@ ----------------------------------------------------------------------------
Writes the styles used by conditional formatting to "styles.xml".
In total there are three parts which must be implemented
for condtional formatting:
#1 Definition of the styles (here, and in WriteConditionalStyle) (can be
omitted if one of the already existing styles is used)
#2 Definition of the cell styles (in WriteCellStyles), style:map nodes)
#3 Definition of the cell ranges in WriteConditionalFormats
(calcext:conditional-formattings node)
-------------------------------------------------------------------------------}
procedure TsSpreadOpenDocWriter.WriteConditionalStyles(AStream: TStream);
var
book: TsWorkbook;
sheet: TsWorksheet;
i, j, k: Integer;
nCF: Integer;
CF: TsConditionalFormat;
fmt: TsCellFormat;
cf_rule: TsCFCellRule;
begin
book := TsWorkbook(FWorkbook);
nCF := book.GetNumConditionalFormats;
for i := 0 to nCF-1 do
begin
CF := book.GetConditionalFormat(i);
// for the moment: write only the style of the highest-priority rule
if CF.Rules[CF.RulesCount-1] is TsCFCellRule then
begin
cf_rule := TsCFCellRule(CF.Rules[CF.RulesCount-1]);
fmt := book.GetCellFormat(cf_rule.FormatIndex);
WriteConditionalStyle(AStream, Format('cf%d', [i]), fmt); // "cf" + index of CF in book's list
end;
end;
(*
for i := 0 to book.GetWorksheetCount-1 do
begin
sheet := book.GetWorksheetByIndex(i);
for j := 0 to sheet.ConditionalFormatCount-1 do
begin
CF := sheet.ReadConditionalFormat(j);
for k := 0 to CF.RulesCount-1 do
begin
rule := CF.Rules[k];
if rule is TsCFCellRule then
begin
fmt := book.GetCellFormat(TsCFCelLRule(rule).FormatIndex);
WriteConditionalStyle(AStream, Format('cf%d_%d', [i, j]), fmt); // cf"sheet"_"fmtindex"
end;
end;
end;
end;
*)
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes the declaration of the font faces used in the workbook. Writes the declaration of the font faces used in the workbook.
Is used in styles.xml and content.xml. Is used in styles.xml and content.xml.
@ -6833,12 +7116,9 @@ begin
Unused(ARow, ACol); Unused(ARow, ACol);
valType := 'boolean'; valType := 'boolean';
lStyle := GetStyleName(ACell);
fmt := (FWorkbook as TsWorkbook).GetCellFormat(ACell^.FormatIndex); if lStyle <> '' then
if fmt.UsedFormattingFields <> [] then lStyle := Format(' table:style-name="%s"', [lStyle]);
lStyle := ' table:style-name="ce' + IntToStr(ACell^.FormatIndex) + '" '
else
lStyle := '';
// Comment // Comment
comment := WriteCommentXMLAsString((FWorksheet as TsWorksheet).ReadComment(ACell)); comment := WriteCommentXMLAsString((FWorksheet as TsWorksheet).ReadComment(ACell));
@ -7020,6 +7300,101 @@ begin
end; end;
end; end;
function TsSpreadOpenDocWriter.WriteConditionalStyleXMLAsString(AFormatIndex: Integer): string;
var
book: TsWorkbook;
isConditional: Boolean;
i, j, k: Integer;
cf: TsConditionalFormat;
cf_CellRule: TsCFCellRule;
cf_StyleName: String;
cf_Condition: String;
cf_Sheet: TsWorksheet;
firstCellOfRange: String;
operand1Str: String;
begin
Result := '';
book := TsWorkbook(FWorkbook);
cf := book.GetConditionalFormat(AFormatIndex);
cf_styleName := 'cf' + IntToStr(AFormatIndex);
cf_sheet := cf.Worksheet as TsWorksheet;
firstCellOfRange := cf_sheet.Name + '.' + GetCellString(cf.CellRange.Row1, cf.CellRange.Col1);
if cf.Rules[cf.RulesCount-1] is TsCFCellRule then
begin
// for the moment: we only use the highest-priority rule
cf_cellRule := TsCFCellRule(cf.Rules[cf.RulesCount-1]);
case cf_cellRule.Condition of
cfcEqual:
begin
operand1Str := VarToStr(cf_cellrule.Operand1);
if VarIsStr(cf_cellRule.Operand1) then
operand1Str := UTF8TextToXMLText(SafeQuoteStr(cf_cellrule.Operand1));
cf_Condition := Format('cell-content()=%s', [operand1Str]);
end;
else
cf_Condition := '';
end;
if cf_Condition <> '' then
Result := Format('<style:map style:condition="%s" style:apply-style-name="%s" style:base-cell-address="%s" />', [
cf_Condition,
cf_StyleName,
firstCellOfRange
]);
end;
(*
// Determine whether the format is a conditional format.
isConditional := false;
for i := 0 to book.GetWorksheetCount-1 do
begin
sheet := book.GetWorksheetByIndex(i);
for j := 0 to sheet.ConditionalFormatCount-1 do
begin
cf := sheet.ReadConditionalFormat(j);
for k := 0 to cf.RulesCount-1 do
if cf.Rules[k] is TsCFCellRule then
if TsCFCellRule(cf.Rules[k]).FormatIndex = AFormatIndex then
begin
isConditional := true;
cf_CellRule := TsCFCellRule(cf.Rules[k]);
cf_StyleName := Format('cf%d_%d', [i, cf_CellRule.FormatIndex]);
firstCellOfRange := GetCellString(cf.CellRange.Row1, cf.CellRange.Col1);
firstCellOfRange := sheet.Name + '.' + firstCellOfRange;
break;
end;
if isConditional then break;
end;
if isConditional then break;
end;
if not isConditional then
exit;
// <style:map style:condition="cell-content()=5" style:apply-style-name="cf" style:base-cell-address="Tabelle1.B4" />
// or ="cell-content()="abc""
case cf_cellRule.Condition of
cfcEqual:
begin
operand1Str := VarToStr(cf_cellrule.Operand1);
if VarIsStr(cf_cellRule.Operand1) then
operand1Str := UTF8TextToXMLText(SafeQuoteStr(cf_cellrule.Operand1));
cf_Condition := Format('cell-content()=%s', [operand1Str]);
end;
else
cf_Condition := '';
end;
if cf_Condition <> '' then
Result := Format('<style:map style:condition="%s" style:apply-style-name="%s" style:base-cell-address="%s" />', [
cf_Condition,
cf_StyleName,
firstCellOfRange
]);
*)
end;
function TsSpreadOpenDocWriter.WriteDefaultFontXMLAsString: String; function TsSpreadOpenDocWriter.WriteDefaultFontXMLAsString: String;
var var
fnt: TsFont; fnt: TsFont;
@ -7624,6 +7999,7 @@ begin
i+1, UTF8TextToXMLText(sheetname), i+1, UTF8TextToXMLText(sheetname),
FALSE_TRUE[not (soHidden in sheet.Options)], bidi, tabColor FALSE_TRUE[not (soHidden in sheet.Options)], bidi, tabColor
])); ]));
if sheet.GetImageCount > 0 then if sheet.GetImageCount > 0 then
begin begin
// Embedded images written by fps refer to a graphic style "gr1"... // Embedded images written by fps refer to a graphic style "gr1"...
@ -8239,6 +8615,10 @@ begin
Unused(ARow, ACol); Unused(ARow, ACol);
valType := 'float'; valType := 'float';
lStyle := GetStyleName(ACell);
if lStyle <> '' then
lStyle := Format(' table:style-name="%s"', [lStyle]);
fmt := (FWorkbook as TsWorkbook).GetCellFormat(ACell^.FormatIndex); fmt := (FWorkbook as TsWorkbook).GetCellFormat(ACell^.FormatIndex);
if fmt.UsedFormattingFields <> [] then if fmt.UsedFormattingFields <> [] then
begin begin
@ -8257,9 +8637,7 @@ begin
if (nfkCurrency in nfSection.Kind) then if (nfkCurrency in nfSection.Kind) then
valtype := 'currency' valtype := 'currency'
end; end;
lStyle := ' table:style-name="ce' + IntToStr(ACell^.FormatIndex) + '"'; end;
end else
lStyle := '';
// Comment // Comment
comment := WriteCommentXMLAsString((FWorksheet as TsWorksheet).ReadComment(ACell)); comment := WriteCommentXMLAsString((FWorksheet as TsWorksheet).ReadComment(ACell));