diff --git a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas index 89c50d7c7..c208b552a 100644 --- a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas +++ b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas @@ -10,7 +10,6 @@ var sh: TsWorksheet; fmt: TsCellFormat; fmtIdx: Integer; - font: TsFont; row: Integer; i: Integer; lastCol: Integer; diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index 529103d0c..9143c3ff2 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -193,12 +193,10 @@ type procedure WriteAutomaticStyles(AStream: TStream); procedure WriteCellRow(AStream: TStream; ASheet: TsBasicWorksheet; ARowIndex, ALastColIndex: Integer); - procedure WriteCellStyle(AStream: TStream; AFormatIndex, AConditionalFormatIndex: integer); procedure WriteCellStyles(AStream: TStream); procedure WriteColStyles(AStream: TStream); 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; ARowIndex, AFirstColIndex, ALastColIndex, ALastRowIndex: Integer; @@ -210,6 +208,8 @@ type procedure WriteRowStyles(AStream: TStream); procedure WriteRowsAndCells(AStream: TStream; ASheet: TsBasicWorksheet); procedure WriteShapes(AStream: TStream; ASheet: TsBasicWorksheet); + procedure WriteStyleNode(AStream: TStream; const AStyleName: String; + const AFormat: TsCellFormat; AConditionalFormatIndex: Integer); procedure WriteTableSettings(AStream: TStream); procedure WriteTableStyles(AStream: TStream); procedure WriteTextStyles(AStream: TStream); @@ -2475,7 +2475,6 @@ var p: Integer; fmt: PsCellFormat; ns: String; - hasFormula: Boolean; begin {$IFDEF FPSpreadDebug} DebugLn(Format('[ReadFormula] ARow=%d, ACol=%d, AStyleIndex=%d', [ARow, ACol, AStyleIndex])); @@ -2490,10 +2489,6 @@ begin cell := TsWorksheet(FWorksheet).GetCell(ARow, ACol); // Don't use AddCell here ApplyStyleToCell(cell, AStyleIndex); - { - styleName := GetAttrValue(ACellNode, 'table:style-name'); - ApplyStyleToCell(cell, stylename); - } fmt := TsWorkbook(Workbook).GetPointerToCellFormat(cell^.FormatIndex); formulaStr := ''; @@ -2520,13 +2515,11 @@ begin formula^.Parser.Expression[fdOpenDocument] := formulaStr; // Parse in ODS dialect formula^.Text := formula^.Parser.Expression[fdExcelA1]; // Convert to Excel A1 dialect cell^.Flags := cell^.Flags + [cfHasFormula]; - hasFormula := true; {$IFDEF FPSpreadDebug} DebugLn(' Formula found: ' + formula); {$ENDIF} - end else - hasFormula := false; + end; // Read formula results @@ -3698,7 +3691,7 @@ procedure TsSpreadOpenDocReader.ReadCellImages(ANode: TDOMNode; ARow, ACol: Cardinal); var childNode: TDOMNode; - nodeName: String; + {%H-}nodeName: String; begin childNode := ANode.FirstChild; while Assigned(childNode) do @@ -5331,6 +5324,87 @@ begin ''); end; +{ Writes the style node in "content.xml" as well as the conditional format + part of a style to "styles.xml". } +procedure TsSpreadopenDocWriter.WriteStyleNode(AStream: TStream; + const AStyleName: String; const AFormat: TsCellFormat; + AConditionalFormatIndex: Integer); +var + s: String; + nfs: String; + nfParams: TsNumFormatParams; + nfIdx: Integer; + j, p: Integer; + addProtection: Boolean; +begin + addProtection := (AConditionalFormatIndex = -1); + + nfs := ''; + nfidx := AFormat.NumberFormatIndex; + if nfidx <> -1 then + begin + nfParams := TsWorkbook(FWorkbook).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; + + AppendToStream(AStream, Format( + '', + [AStyleName, nfs] + )); + + // style:text-properties ---> font + s := WriteFontStyleXMLAsString(AFormat); + if s <> '' then + AppendToStream(AStream, + ''); + + // - border, background, wordwrap, text rotation, vertical alignment + s := WriteBorderStyleXMLAsString(AFormat) + + WriteBackgroundColorStyleXMLAsString(AFormat) + + WriteWordwrapStyleXMLAsString(AFormat) + + WriteTextRotationStyleXMLAsString(AFormat) + + WriteVertAlignmentStyleXMLAsString(AFormat); + if addProtection then + s := s + WriteCellProtectionStyleXMLAsString(AFormat); + if s <> '' then + AppendToStream(AStream, + ''); + + // style:paragraph-properties ---> hor alignment, bidi + s := WriteHorAlignmentStyleXMLAsString(AFormat) + + WriteBiDiModeStyleXMLAsString(AFormat); + if s <> '' then + AppendToStream(AStream, + ''); + + if (AConditionalFormatIndex > -1) then + begin + s := WriteConditionalStyleXMLAsString(AConditionalFormatIndex); + if s <> '' then + AppendToStream(AStream, s); + end; + + AppendToStream(AStream, + ''); +end; + { Writes the file "styles.xml" } procedure TsSpreadOpenDocWriter.WriteStyles; begin @@ -5531,104 +5605,6 @@ begin ''); end; -{ Writes the style node in "content.xml" } -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. - // In case of a conditional format the style name is a combination of - // conditional format index and normal format index. - if isConditionalFormat then - styleName := 'ce' + IntToStr(1000 * (AConditionalFormatIndex+1) + AFormatIndex) - else - styleName := 'ce' + IntToStr(AFormatIndex); - - 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:text-properties - // - font - s := WriteFontStyleXMLAsString(fmt); - if s <> '' then - AppendToStream(AStream, - ''); - - // - 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:paragraph-properties - // - hor alignment, bidi - s := WriteHorAlignmentStyleXMLAsString(fmt) + - WriteBiDiModeStyleXMLAsString(fmt); - if s <> '' then - AppendToStream(AStream, - ''); - - if isConditionalFormat then - begin - s := WriteConditionalStyleXMLAsString(AConditionalFormatIndex); - if s <> '' then - AppendToStream(AStream, s); - end; - - // End - AppendToStream(AStream, - ''); -end; - { Writes the cell styles ("ce0", "ce1", ...). Directly maps to the CellFormats list of the workbook. "ce0" is the default format } @@ -5638,20 +5614,24 @@ var cf: TsConditionalFormat; cf_sheet: TsWorksheet; cf_range: TsCellRange; - cf_rule: TsCFCellRule; ncf: Integer; - i, j: Integer; + i: Integer; cell: PCell; r, c: Cardinal; L: TStrings; s: String; - fmtIndex, cfIndex: Integer; + styleName: String; + fmtIndex: Integer = 0; + cfIndex: Integer = 0; begin book := TsWorkbook(FWorkbook); // Write fixed formats only for i := 0 to book.GetNumCellFormats - 1 do - WriteCellStyle(AStream, i, -1); + begin + styleName := 'ce' + IntToStr(i); + WriteStyleNode(AStream, styleName, book.GetCellFormat(i), -1); + end; // Conditional formats contain the fixed formats plus the condition params // To avoid duplicate style entries in the file we first collect all style @@ -5672,22 +5652,25 @@ begin begin cell := cf_sheet.FindCell(r, c); if Assigned(cell) and - (cell^.ConditionalFormatIndex[High(cell^.ConditionalFormatIndex)] = i) + (cell^.ConditionalFormatIndex[High(cell^.ConditionalFormatIndex)] = i) then begin s := 'ce' + IntToStr((i+1) * 1000 + cell^.FormatIndex); + // To distinguish conditional from fixed format numbers we increment + // cfIndex by 1 to have thousands in the number even when cfIndex = 0. if L.IndexOf(s) = -1 then L.Add(s); end; end; end; - // Now write the combined styles to the stream. The styles can be identified - // from the style name in the string list. + // Now write the combined styles to the stream. The style names were stored + // in the unique way in the string list; the format index can be extracted + // from the style name. for i := 0 to L.Count-1 do begin + styleName := L.Strings[i]; s := Copy(L[i], 3, MaxInt); // remove 'ce' - j := StrToInt(s); - DivMod(j, 1000, cfIndex, fmtIndex); - WriteCellStyle(AStream, fmtIndex, cfIndex-1); + DivMod(StrToInt(s), 1000, cfIndex, fmtIndex); // extract cfIndex and fmt Index from style name + WriteStyleNode(AStream, styleName, book.GetCellFormat(fmtIndex), cfIndex-1); // cfIndex was incremented by 1. end; finally L.Free; @@ -5698,10 +5681,7 @@ procedure TsSpreadOpenDocWriter.WriteColStyles(AStream: TStream); var i: Integer; colstyle: TColumnStyleData; - book: TsWorkbook; begin - book := TsWorkbook(FWorkbook); - if FColumnStyleList.Count = 0 then begin AppendToStream(AStream, @@ -5911,13 +5891,12 @@ var cf_range: TsCellRange; cf_styleName: String; cf_cellRule: TsCFCellRule; - i, j, k: Integer; + i,j: Integer; sheet: TsWorksheet; rangeStr: String; firstCellStr: string; value1Str, value2Str: String; opStr: String; - s: String; begin book := TsWorkbook(FWorkbook); sheet := TsWorksheet(ASheet); @@ -5939,27 +5918,17 @@ begin rangeStr ])); - for k := 0 to cf.RulesCount-1 do + for j := 0 to cf.RulesCount-1 do begin - value1Str := ''; - value2Str := ''; - opStr := ''; - if cf.Rules[k] is TsCFCellRule then + if cf.Rules[j] is TsCFCellRule then begin - cf_cellRule := TsCFCellRule(cf.Rules[k]); + cf_cellRule := TsCFCellRule(cf.Rules[j]); cf_styleName := Format('conditional_%d', [cf_CellRule.FormatIndex]); value1Str := CFOperandToStr(cf_cellRule.Operand1, sheet); value2Str := CFOperandToStr(cf_cellRule.Operand2, sheet); opStr := Format(CF_CALCEXT_OP[cf_cellRule.Condition], [value1Str, value2str]); if opStr <> '' then begin - // Fix formula syntax - s := VarToStr(cf_cellRule.Operand1); - { - if (Length(s) > 1) and (s[1] = '=') then - opStr := 'of:' + opStr; - } - // construct calcext string AppendToStream(AStream, Format( '', [cf_stylename, opStr, firstCellStr] @@ -5975,47 +5944,22 @@ begin '' ); end; -{ Writes the conditional format part of a style to "styles.xml". } -procedure TsSpreadopenDocWriter.WriteConditionalStyle(AStream: TStream; - AStyleName: String; const AFormat: TsCellFormat); -var - s: String; -begin - AppendToStream(AStream, Format( - '', - [AStyleName])); - - AppendToStream(AStream, Format( - '', [ - WriteBackgroundColorStyleXMLAsString(AFormat), - WriteBorderStyleXMLAsString(AFormat) - // To do: add the remaining style elements - ])); - - s := WriteFontStyleXMLAsString(AFormat); - if s <> '' then - AppendToStream(AStream, - ''); - - AppendToStream(AStream, - ''); -end; - {@@ ---------------------------------------------------------------------------- Writes the styles used by conditional formatting to "styles.xml". - In total there are three parts which must be implemented + In total there are four 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 + #1 Definition of the styles (here, and in WriteStyleNode) (can be omitted if + one of the already existing styles is used) + #2 Definition of the cell styles in contents.xml + (in WriteCellStyles), style:map nodes) + #3 Definition of the cell ranges in WriteConditionalFormats in content.xml (calcext:conditional-formattings node) + #4 Find the correct style when cells are written in content.xml -------------------------------------------------------------------------------} procedure TsSpreadOpenDocWriter.WriteConditionalStyles(AStream: TStream); var book: TsWorkbook; - sheet: TsWorksheet; - i, j, k: Integer; + i, j: Integer; nCF: Integer; CF: TsConditionalFormat; fmt: TsCellFormat; @@ -6041,7 +5985,7 @@ begin fmt := book.GetCellFormat(TsCFCellRule(cf_Rule).FormatIndex); stylename := Format('conditional_%d', [fmtIndex]); if L.IndexOf(styleName) = -1 then begin - WriteConditionalStyle(AStream, stylename, fmt); + WriteStyleNode(AStream, stylename, fmt, i); L.Add(styleName); end; end; @@ -6600,7 +6544,6 @@ var col: PCol; cell: PCell; stylename: string; - h: Single; firstcol: Integer; lastcol: Integer; c, cc: integer; @@ -7179,7 +7122,6 @@ var comment: String; strValue: String; displayStr: String; - fmt: TsCellFormat; begin Unused(ARow, ACol); @@ -7381,7 +7323,6 @@ var cf_Sheet: TsWorksheet; firstCellOfRange: String; operand1Str, operand2Str: String; - s: String; begin Result := ''; @@ -7401,13 +7342,6 @@ begin cf_condition := Format(CF_STYLE_OP[cf_cellRule.Condition], [operand1Str, operand2Str]); if cf_Condition <> '' then begin - // Fix formula syntax - (* - s := VarToStr(cf_cellRule.Operand1); - if (Length(s) > 1) and (s[1] = '=') then - cf_condition := 'of:' + cf_Condition; - *) - // Build style:map string Result := Result + Format('', [ cf_Condition, @@ -7478,7 +7412,6 @@ end; procedure TsSpreadOpenDocWriter.WriteError(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell); var - fmt: PsCellFormat; lStyle: String; comment: String; rowsSpannedStr, colsSpannedStr: String; @@ -8281,7 +8214,6 @@ var spannedStr: String; comment: String; r1,c1,r2,c2: Cardinal; - fmt: TsCellFormat; ignoreFormulas: Boolean; sheet: TsWorksheet; oldDialect: TsFormulaDialect; @@ -8426,7 +8358,6 @@ var spannedStr: String; r1,c1,r2,c2: Cardinal; totaltxt, target, bookmark, comment: String; - fmt: TsCellFormat; fnt: TsFont; fntName: String; hyperlink: PsHyperlink; diff --git a/components/fpspreadsheet/source/common/fpstypes.pas b/components/fpspreadsheet/source/common/fpstypes.pas index 3a83a6a1d..e3f10c778 100644 --- a/components/fpspreadsheet/source/common/fpstypes.pas +++ b/components/fpspreadsheet/source/common/fpstypes.pas @@ -726,6 +726,9 @@ type procedure SetBorders(ABorders: TsCellBorders; AColor: TsColor = scBlack; ALineStyle: TsLineStyle = lsThin); procedure SetFont(AFontIndex: Integer); + procedure SetHorAlignment(AHorAlign: TsHorAlignment); + procedure SetTextRotation(ARotation: TsTextRotation); + procedure SetVertAlignment(AVertAlign: TsVertAlignment); end; {@@ Pointer to a format record } @@ -1112,6 +1115,24 @@ begin UsedFormattingFields := UsedFormattingFields + [uffFont]; end; +procedure TsCellFormat.SetHorAlignment(AHorAlign: TsHorAlignment); +begin + HorAlignment := AHorAlign; + UsedFormattingFields := usedFormattingFields + [uffHorAlign]; +end; + +procedure TsCellFormat.SetTextRotation(ARotation: TsTextRotation); +begin + TextRotation := ARotation; + UsedFormattingFields := UsedFormattingFields + [uffTextRotation]; +end; + +procedure TsCellFormat.SetVertAlignment(AVertAlign: TsVertAlignment); +begin + VertAlignment := AVertAlign; + UsedFormattingfields := UsedFormattingFields + [uffVertAlign]; +end; + { TsFont }