diff --git a/components/fpspreadsheet/source/common/fpsexprparser.pas b/components/fpspreadsheet/source/common/fpsexprparser.pas index f50e6f165..4a9aac514 100644 --- a/components/fpspreadsheet/source/common/fpsexprparser.pas +++ b/components/fpspreadsheet/source/common/fpsexprparser.pas @@ -868,6 +868,9 @@ procedure RegisterFunction(const AName: ShortString; const AResultType: Char; procedure RegisterFunction(const AName: ShortString; const AResultType: Char; const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionEvent); overload; +function ConvertFormulaDialect(AFormula: String; + ASrcDialect, ADestDialect: TsFormulaDialect; AWorksheet: TsBasicWorksheet): String; + var // Format settings used in stored parsed formulas. ExprFormatSettings: TFormatSettings; @@ -4903,6 +4906,40 @@ begin Result.ResString := AValue; end; + +function ConvertFormulaDialect(AFormula: String; + ASrcDialect, ADestDialect: TsFormulaDialect; AWorksheet: TsBasicWorksheet): String; +var + parser: TsSpreadsheetParser; +begin + if ASrcDialect = ADestDialect then + begin + Result := AFormula; + exit; + end; + + if (ASrcDialect = fdExcelR1C1) or (ADestDialect = fdExcelR1C1) then + raise Exception.Create('ConvertFormulaDialect cannot be used for Excel R1C1 syntax.'); + + parser := TsSpreadsheetParser.Create(AWorksheet); + try + try + parser.Expression[ASrcDialect] := AFormula; // Parse in source dialect + Result := parser.Expression[ADestDialect]; // Convert to destination dialect + except + on EGeneralExprParserError do + begin + Result := AFormula; + (AWorksheet as TsWorksheet).Workbook.AddErrorMsg('Error converting formula "' + AFormula + '"'); + end; + end; + finally + parser.Free; + end; + +end; + + {@@ --------------------------------------------------------------------------- Registers a non-built-in function: diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index 13016c140..0d4738f4d 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -115,8 +115,10 @@ type procedure ReadCell(ANode: TDOMNode; ARow, ACol: Integer; AFormatIndex: Integer; out AColsRepeated: Integer); procedure ReadCellImages(ANode: TDOMNode; ARow, ACol: Cardinal); + procedure ReadCFCellFormat(ANode: TDOMNode; ASheet: TsBasicWorksheet; ARange: TsCellRange); procedure ReadColumns(ATableNode: TDOMNode); procedure ReadColumnStyle(AStyleNode: TDOMNode); + procedure ReadConditionalFormats(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); procedure ReadDateMode(SpreadSheetNode: TDOMNode); procedure ReadDocumentProtection(ANode: TDOMNode); procedure ReadFont(ANode: TDOMNode; var AFontName: String; @@ -424,7 +426,6 @@ const function CFOperandToStr(v: variant; AWorksheet: TsWorksheet): String; var r,c: Cardinal; - parser: TsSpreadsheetParser; begin Result := VarToStr(v); if Result = '' then @@ -433,23 +434,7 @@ begin if VarIsStr(v) then begin // Special case: v is a formula, i.e. begins with '=' if (Length(Result) > 1) and (Result[1] = '=') then - begin - parser := TsSpreadsheetParser.Create(AWorksheet); - try - try - parser.Expression[fdExcelA1] := Result; // Parse in Excel-A1 dialect - Result := parser.Expression[fdOpenDocument]; // Convert to ODS dialect - except - on EGeneralExprParserError do - begin - Result := VarToStr(v); - AWorksheet.Workbook.AddErrorMsg('Error in CF Expression ' + Result); - end; - end; - finally - parser.Free; - end; - end + Result := ConvertFormulaDialect(Result, fdExcelA1, fdOpenDocument, AWorksheet) else // Special case: cell reference (Note: relative refs are made absolute!) if ParseCellString(Result, r, c) then @@ -2169,6 +2154,51 @@ begin (FWorksheet as TsWorksheet).WriteComment(ARow, ACol, comment); end; +procedure TsSpreadOpenDocReader.ReadConditionalFormats(ANode: TDOMNode; + AWorksheet: TsBasicWorksheet); +var + sheet: TsWorksheet; + childNode: TDOMNode; + nodeName: String; + s: String; + range: TsCellRange = (Row1: Cardinal(-1); Col1: Cardinal(-1); Row2: Cardinal(-1); Col2: Cardinal(-1)); + sheet1, sheet2: string; + flags: TsRelFlags; +begin + ANode := ANode.FindNode('calcext:conditional-formats'); + if ANode = nil then + exit; + + sheet := TsWorksheet(AWorksheet); + ANode := ANode.FirstChild; + while ANode <> nil do + begin + nodeName := ANode.NodeName; + if nodeName = 'calcext:conditional-format' then + begin + // Cell range + s := GetAttrValue(ANode, 'calcext:target-range-address'); + if (s = '') or not TryStrToCellRange_ODS(s, sheet1, sheet2, range.Row1, range.Col1, range.Row2, range.Col2, flags) then + begin + ANode := ANode.NextSibling; + Continue; + end; + + childNode := ANode.FirstChild; + while childNode <> nil do + begin + nodeName := childNode.NodeName; + + if nodeName = 'calcext:condition' then + ReadCFCellFormat(childNode, AWorksheet, range); + + childNode := childNode.NextSibling; + end; + end; + ANode := ANode.NextSibling; + end; +end; + procedure TsSpreadOpenDocReader.ReadDateTime(ARow, ACol: Cardinal; AStyleIndex: Integer; ACellNode: TDOMNode); var @@ -2728,6 +2758,8 @@ begin ReadColumns(TableNode); // Process each row inside the sheet and process each cell of the row ReadRowsAndCells(TableNode); + // Read conditional formats + ReadConditionalFormats(TableNode, FWorksheet); // Read page layout ReadPageLayout(StylesNode, GetAttrValue(TableNode, 'table:style-name'), (FWorksheet as TsWorksheet).PageLayout); @@ -3719,6 +3751,183 @@ begin end; end; +function ExtractArguments(s: String; IsExpression: Boolean; + var arg1, arg2: String): Boolean; +var + p: Integer; + sa: TStringArray; +begin + if s = '' then + raise Exception.Create('Empty string not allowed'); + + if IsExpression then + begin + p := pos('(', s); + if p = 0 then + exit(false); + Delete(s, 1, p); + Delete(s, Length(s), 1); + arg1 := UnquoteStr(s); + exit(true); + end; + + case s[1] of + '=': arg1 := UnquoteStr(Copy(s, 2, MaxInt)); + '<', '>', '!': + if (s[2] = '=') then + arg1 := UnquoteStr(Copy(s, 3, MaxInt)) + else + arg1 := UnquoteStr(Copy(s, 2, MaxInt)); + else + p := pos('(', s); + if p = 0 then + exit(false); + Delete(s, 1, p); + + p := pos(')', s); + if p = 0 then + exit(false); + Delete(s, p, MaxInt); + + sa := s.Split(','); + arg1 := UnquoteStr(sa[0]); + if Length(sa) > 1 then + arg2 := UnquoteStr(sa[1]); + end; + Result := true; +end; + +procedure TsSpreadOpenDocReader.ReadCFCellFormat(ANode: TDOMNode; + ASheet: TsBasicWorksheet; ARange: TsCellRange); +const + CONDITONS_WITHOUT_ARGUMENTS: set of TsCFCondition = [ + cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, + cfcUnique, cfcDuplicate, + cfcContainsErrors, cfcNotContainsErrors + ]; +var + s: String; + fmtIndex: Integer; + fmt: TsCellFormat; + ok: Boolean; + condition: TsCFCondition; + param1, param2: String; + op1, op2: Variant; + sheet: TsWorksheet; +begin + sheet := TsWorksheet(ASheet); + + // Style + s := GetAttrValue(ANode, 'calcext:apply-style-name'); + if s <> '' then + begin + fmtIndex := ExtractFormatIndexFromStyle(s, -1); + fmt := FCellFormatList.Items[fmtIndex]^; + fmtIndex := (FWorkbook as TsWorkbook).AddCellFormat(fmt); + end; + + // Type of CF, operation + s := GetAttrValue(ANode, 'calcext:value'); + if s = '' then + exit; + + ok := true; + case s[1] of + '=': condition := cfcEqual; + '!': if (s[2] = '=') then + condition := cfcNotEqual + else + ok := false; + '<': if (s[2] = '=') then + condition := cfcLessEqual + else + condition := cfcLessThan; + '>': if s[2] = '=' then + condition := cfcGreaterEqual + else + condition := cfcGreaterThan; + 'a': case s of + 'above-average': condition := cfcAboveAverage; + 'above-equal-average': condition := cfcAboveEqualAverage + end; + 'b': case s of + 'below-average': condition := cfcBelowAverage; + 'below-equal-average': condition := cfcBelowEqualAverage; + else + if pos('begins-with(', s) = 1 then + condition := cfcBeginsWith + else + if pos('between(', s) = 1 then + condition := cfcBetween + else + if pos('bottom-elements(', s) = 1 then + condition := cfcBottom + else + if pos('bottom-percent(', s) = 1 then + condition := cfcBottomPercent + else + ok := false; + end; + 'c': if pos('contains-text(', s) = 1 then + condition := cfcContainsText + else + ok := false; + 'd': if s = 'duplicate' then + condition := cfcDuplicate + else + ok := false; + 'e': if pos('ends-with(', s) = 1 then + condition := cfcEndsWith + else + ok := false; + 'f': if pos('formula-is(', s) = 1 then + condition := cfcExpression + else + ok := false; + 'i': if (s = 'is-error') then + condition := cfcContainsErrors + else if s = 'is-no-error' then + condition := cfcNotContainsErrors + else + ok := false; + 'n': if pos('not-contains-text(', s) = 1 then + condition := cfcNotContainsText + else if pos('not-between(', s) = 1 then + condition := cfcNotBetween + else + ok := false; + 't': if pos('top-elements(', s) = 1 then + condition := cfcTop + else if pos('top-percent(', s) = 1 then + condition := cfcTopPercent + else + ok := false; + 'u' : if s = 'unique' then + condition := cfcUnique + else + ok := false; + end; + + if ok then + begin + if (condition in CONDITONS_WITHOUT_ARGUMENTS) then + ok := true + else if (condition = cfcExpression) then + begin + ok := ExtractArguments(s, true, param1, param2); + param1 := ConvertFormulaDialect(param1, fdOpenDocument, fdExcelA1, sheet); + end else + ok := ExtractArguments(s, false, param1, param2); + end; + + if not ok then + exit; + + if param1 = '' then VarClear(op1) else op1 := param1; + if param2 = '' then VarClear(op2) else op2 := param2; + sheet.WriteConditionalCellFormat(ARange, condition, op1, op2, fmtIndex); +end; + { Reads the cells in the given table. Loops through all rows, and then finds all cells of each row. } procedure TsSpreadOpenDocReader.ReadRowsAndCells(ATableNode: TDOMNode); diff --git a/components/fpspreadsheet/source/common/fpsutils.pas b/components/fpspreadsheet/source/common/fpsutils.pas index f17b4df0c..59d9e04ee 100644 --- a/components/fpspreadsheet/source/common/fpsutils.pas +++ b/components/fpspreadsheet/source/common/fpsutils.pas @@ -121,6 +121,9 @@ function GetCellRangeString_R1C1(ASheet1, ASheet2: String; function SheetNameNeedsQuotes(ASheet: String): Boolean; // OpenDocument Syntax +function TryStrToCellRange_ODS(const AStr: String; out ASheet1, ASheet2: String; + out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags): Boolean; + function GetCellRangeString_ODS(ASheet1, ASheet2: String; ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = rfAllRel): String; overload; function GetCellRangeString_ODS(ARow1, ACol1, ARow2, ACol2: Cardinal; @@ -1308,6 +1311,65 @@ begin end; +{@@ ---------------------------------------------------------------------------- + Extracts sheets names and cell coordinates from a cell range string in + OpenDocument syntax, e.g. "Table1.A1:Table2.B4" +-------------------------------------------------------------------------------} +function TryStrToCellRange_ODS(const AStr: String; out ASheet1, ASheet2: String; + out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags): Boolean; +var + p: Integer; + cell1Str, cell2Str: String; + f: TsRelFlags; +begin + p := Pos(':', AStr); + if p = 0 then + begin + cell1Str := AStr; + cell2Str := ''; + end else + begin + cell1str := Copy(AStr, 1, p-1); + cell2Str := Copy(AStr, p+1, MaxInt); + end; + + p := pos('.', cell1Str); + if p = 0 then + ASheet1 := '' + else + begin + ASheet1 := Copy(cell1Str, 1, p-1); + cell1Str := Copy(cell1Str, p+1, MaxInt); + end; + Result := ParseCellString(cell1Str, ARow1, ACol1, AFlags); + + if not Result then + exit; + + if cell2Str <> '' then + begin + p := pos('.', cell2Str); + if p = 0 then + ASheet2 := '' + else + begin + ASheet2 := Copy(cell2Str, 1, p-1); + cell2Str := Copy(cell2Str, p+1, MaxInt); + end; + Result := ParseCellString(cell2Str, ARow2, ACol2, f); + if (rfRelRow in f) then Include(AFlags, rfRelRow2); + if (rfRelCol in f) then Include(Aflags, rfRelCol2); + end else + begin + ASheet2 := ASheet1; + ARow2 := ARow1; + ACol2 := ACol2; + if (rfRelRow in AFlags) then Include(AFlags, rfRelRow2); + if (rfRelCol in AFlags) then Include(AFlags, rfRelCol2); + end; +end; + + {@@ ---------------------------------------------------------------------------- Calculates a cell range string with sheet specification in OpenDocument syntax -------------------------------------------------------------------------------} diff --git a/components/fpspreadsheet/tests/conditionalformattests.pas b/components/fpspreadsheet/tests/conditionalformattests.pas index a488b330d..d29c00b4d 100644 --- a/components/fpspreadsheet/tests/conditionalformattests.pas +++ b/components/fpspreadsheet/tests/conditionalformattests.pas @@ -111,6 +111,37 @@ type procedure TestWriteRead_CF_CellFmt_XML_Border2; procedure TestWriteRead_CF_CellFmt_XML_Font; + { OpenDocument ODS } + procedure TestWriteRead_CF_CellFmt_ODS_Equal_Const; + procedure TestWriteRead_CF_CellFmt_ODS_NotEqual_Const; + procedure TestWriteRead_CF_CellFmt_ODS_GreaterThan_Const; + procedure TestWriteRead_CF_CellFmt_ODS_LessThan_Const; + procedure TestWriteRead_CF_CellFmt_ODS_GreaterEqual_Const; + procedure TestWriteRead_CF_CellFmt_ODS_LessEqual_Const; + procedure TestWriteRead_CF_CellFmt_ODS_Between_Const; + procedure TestWriteRead_CF_CellFmt_ODS_NotBetween_Const; + procedure TestWriteRead_CF_CellFmt_ODS_AboveAverage; + procedure TestWriteRead_CF_CellFmt_ODS_BelowAverage; + procedure TestWriteRead_CF_CellFmt_ODS_AboveEqualAverage; + procedure TestWriteRead_CF_CellFmt_ODS_BelowEqualAverage; +// procedure TestWriteRead_CF_CellFmt_ODS_AboveAverage_2StdDev; // not supported by ODS +// procedure TestWriteRead_CF_CellFmt_ODS_BelowAverage_2StdDev; + procedure TestWriteRead_CF_CellFmt_ODS_Top3; + procedure TestWriteRead_CF_CellFmt_ODS_Top10Percent; + procedure TestWriteRead_CF_CellFmt_ODS_Bottom3; + procedure TestWriteRead_CF_CellFmt_ODS_Bottom10Percent; + procedure TestWriteRead_CF_CellFmt_ODS_BeginsWith; + procedure TestWriteRead_CF_CellFmt_ODS_EndsWith; + procedure TestWriteRead_CF_CellFmt_ODS_Contains; + procedure TestWriteRead_CF_CellFmt_ODS_NotContains; + procedure TestWriteRead_CF_CellFmt_ODS_Unique; + procedure TestWriteRead_CF_CellFmt_ODS_Duplicate; + procedure TestWriteRead_CF_CellFmt_ODS_ContainsErrors; + procedure TestWriteRead_CF_CellFmt_ODS_NotContainsErrors; + procedure TestWriteRead_CF_CellFmt_ODS_Expression; + procedure TestWriteRead_CF_CellFmt_ODS_Background; + procedure TestWriteRead_CF_CellFmt_ODS_Border4; + procedure TestWriteRead_CF_CellFmt_ODS_Border2; end; implementation @@ -901,6 +932,281 @@ begin end; +{ OpenDocument ODS } + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Equal_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEqual, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_NotEqual_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcNotEqual, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_GreaterThan_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcGreaterThan, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_LessThan_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcLessThan, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_GreaterEqual_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcGreaterEqual, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_LessEqual_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcLessEqual, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Between_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBetween, 3, 7, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_NotBetween_Const; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcNotBetween, 3, 7, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_AboveAverage; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcAboveAverage, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_BelowAverage; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBelowAverage, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_AboveEqualAverage; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcAboveEqualAverage, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_BelowEqualAverage; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBelowEqualAverage, fmt); +end; + +{ not supported by ODS +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_AboveAverage_2StdDev; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcAboveAverage, 2.0, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_BelowAverage_2StdDev; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBelowAverage, 2.0, fmt); +end; +} + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Top3; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcTop, 3, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Top10Percent; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcTopPercent, 10, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Bottom3; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBottom, 3, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Bottom10Percent; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBottomPercent, 10, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_BeginsWith; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcBeginsWith, 'ab', fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_EndsWith; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEndsWith, 'kl', fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Contains; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEndsWith, 'b', fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_NotContains; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEndsWith, 'b', fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Unique; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcUnique, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Duplicate; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcDuplicate, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_ContainsErrors; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcContainsErrors, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_NotContainsErrors; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcNotContainsErrors, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Expression; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcExpression, 'ISNUMBER($A$1)', fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Background; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBackground(fsHatchDiag, scYellow, scRed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEqual, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Border4; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBorders([cbNorth, cbEast, cbSouth, cbWest], scBlue, lsDotted); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEqual, 5, fmt); +end; + +procedure TSpreadWriteReadCFTests.TestWriteRead_CF_CellFmt_ODS_Border2; +var + fmt: TsCellFormat; +begin + InitFormatRecord(fmt); + fmt.SetBorders([cbNorth,cbSouth], scBlue, lsDashed); + TestWriteRead_CF_CellFmt(sfOpenDocument, cfcEqual, 5, fmt); +end; + + {------------------------------------------------------------------------------- Color range tests --------------------------------------------------------------------------------}