From 08d598d94134d0203757ffad45fd84c2036a4ed8 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 2 Sep 2026 11:15:38 +0200 Subject: [PATCH] fix: write data bar cfvo as min/max instead of num for lowest/highest value DataBar().LowestValue()/HighestValue() emitted the x14 extension cfvo nodes as type="num" with a formula of 0 instead of type="min"/"max". Strict readers such as WPS Spreadsheets honor that value and lock the bar thresholds to 0, so the data bar renders incorrectly. The extension converter unconditionally overwrote the cfvo type with Numeric whenever a threshold value was present. LowestValue()/HighestValue() store a placeholder "0", so their automatic min/max bounds were turned into fixed numeric thresholds; the same override also collapsed percent, percentile and formula thresholds to num. Each cfvo is now built from its content type: automatic bounds stay autoMin/autoMax, lowest/highest bounds become min/max with no formula, and value based thresholds keep their type and emit the formula. Fixes #2843 --- .../ConditionalFormatTests.cs | 43 ++++++++++ .../ConditionalFormatting/CFDataBar.xlsx | Bin 6634 -> 6616 bytes .../CFDataBarNegative.xlsx | Bin 6749 -> 6733 bytes .../ConditionalFormatting/CFDataBars.xlsx | Bin 7278 -> 7281 bytes .../Save/XLCFDataBarConverterExtension.cs | 75 +++++++++++++----- 5 files changed, 96 insertions(+), 22 deletions(-) diff --git a/ClosedXML.Tests/Excel/ConditionalFormats/ConditionalFormatTests.cs b/ClosedXML.Tests/Excel/ConditionalFormats/ConditionalFormatTests.cs index d63198c..3c98963 100644 --- a/ClosedXML.Tests/Excel/ConditionalFormats/ConditionalFormatTests.cs +++ b/ClosedXML.Tests/Excel/ConditionalFormats/ConditionalFormatTests.cs @@ -3,14 +3,57 @@ using ClosedXML.Excel; using NUnit.Framework; using System.Globalization; using System.IO; +using System.IO.Packaging; using System.Linq; using System.Threading; +using System.Xml.Linq; +using SaveOptions = ClosedXML.Excel.SaveOptions; namespace ClosedXML.Tests.Excel.ConditionalFormats { [TestFixture] public class ConditionalFormatTests { + [Test] + public void DataBarLowestHighestValueWritesMinMaxCfvoWithoutFormula() + { + // https://github.com/ClosedXML/ClosedXML/issues/2843 + // DataBar().LowestValue()/HighestValue() must emit the x14 cfvo nodes as + // type="min"/type="max" without an xm:f formula. The regression wrote them + // as type="num" locked to 0, which strict parsers (e.g. WPS) render wrong. + XNamespace x14 = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"; + + using var ms = new MemoryStream(); + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + ws.Cell("A1").Value = 1; + ws.Cell("A2").Value = -1; + ws.Cell("A3").Value = 2; + ws.Range("A1:A3").AddConditionalFormat() + .DataBar(XLColor.Red) + .LowestValue() + .HighestValue(); + wb.SaveAs(ms); + } + + ms.Position = 0; + using var package = Package.Open(ms, FileMode.Open, FileAccess.Read); + var part = package.GetPart(new Uri("/xl/worksheets/sheet1.xml", UriKind.Relative)); + using var partStream = part.GetStream(); + var doc = XDocument.Load(partStream); + + var x14DataBar = doc.Descendants(x14 + "dataBar").Single(); + var cfvos = x14DataBar.Elements(x14 + "cfvo").ToList(); + + Assert.AreEqual(2, cfvos.Count); + Assert.AreEqual("min", cfvos[0].Attribute("type")?.Value); + Assert.AreEqual("max", cfvos[1].Attribute("type")?.Value); + Assert.IsFalse( + x14DataBar.Descendants().Any(e => e.Name.LocalName == "f"), + "x14 cfvo for lowest/highest value must not contain a formula."); + } + [Test] public void MaintainConditionalFormattingOrder() { diff --git a/ClosedXML.Tests/Resource/Examples/ConditionalFormatting/CFDataBar.xlsx b/ClosedXML.Tests/Resource/Examples/ConditionalFormatting/CFDataBar.xlsx index fe7d0f22fbaee1189c8d0ba8b29a70e5860cef72..b2ded29008f420f3f1d89ec126c463c41ba363d9 100644 GIT binary patch delta 4527 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 F{R?|w&cFZw diff --git a/ClosedXML.Tests/Resource/Examples/ConditionalFormatting/CFDataBarNegative.xlsx b/ClosedXML.Tests/Resource/Examples/ConditionalFormatting/CFDataBarNegative.xlsx index afae098d1cc604822851e2979560c403aae6ee43..5b4b614df03bff59afe91376e3481a8689e6ab96 100644 GIT binary patch delta 4619 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⪼{JLl!G&5jkq%Z8S+#!#{P^Mjd8`n}?{7B_+i zy_?ep4rn#jKB%B5K|`o^!~p)hW~_B$YH(N%5GO{@V;Tt|2u6Ob6d|~RM@4LV-0S(# zNdZ@a3LQ|kDzTR|uO6VC6NyQwzC>G)Zq{|as#K!f90|>IyHZE9W;(&cr(Ga|yOm|j z`iu>neA03hOgL5*H+`u{&MeD+!;szg%8}QsGVk7TqKyx=nv>nmnT{f%A$@rMW>As^cqqR! zW1_ z=KR{`t4XK?nf*+F(WXebN~p`eUWI6GWf5)%U28{q3X z6weOQ!~~Pd9);|F!p`Sj58OR)!eBxO4ODF+I}MLGhLbdWva<7t*`W^Pvl2ce zxsFe*+Ql5z@I-HWiuTWnglQecjr~jtkx0&GrKeQj#85DO)IK~+RU>2OI!3GV_^W}Q zXJ0XB_IXLny+CVqpNjq>_YpcUqKCn(p1anc=|EbDZ-T1_3GpL$EYhN=DF`I~0HJwT z`IbxQhJQ6ki~4#txIrfUrw9Er!bNhs>NE7&&13e%Yz_@kU;s;ac_X07i{gv^}9o*md z=fYjPy>SLm6ZY4|Db*^HPWGmmV5R$QR$oIH0GLfR#u<<*x2;XYv>co)(=y6HZOFnpUP?T{^0&c}vnu+Re@);D` z)>`H7mHa;AM`Cq)5Q4j*voH{WFCp%i-TCCvRT#sZv9?ys!Z;~&q|*n`)5sL8pJA$R zbl=>+x^xnfHR!)$eidV?)?vb6b1I*F+e+62?d$)#_pkiRu!>n{x!%tE;~5?8l+iX+ zprWuiyU(3hu*HTRa}D)rp;ZF<^_E*TYGBGzzgJXX@oxfur^I?Ov7pJn27cEc%N?tI z-nel7+;8+5T<3++V(H`5kW7UoxLTJDOh(xbd6^6<)P;WkO(834P>l0jXAZR;!&>y$>L=0aWTuFzV`qCC|*PwjkgbCp#DE}YL-#DUkDjDn<-Qu0+w<{;e%H6>qlR7cO2>!vdN)np zR0avgO6fe4s!)&-Q3xDxTbZ{A3E}AV$7i{`P0=C5?f~4W+Y?o3AVt~g$5ZqFeX9e0 z#eQRRgU#3jS}6_*dpo$e3=Am_MM8U-*b(Pg-U4MGNHkF*hY zkU%)t%W!jwVTZwH*zU{I*Z+?th-Ys<&H(YbhO`yFSrFlz>|5>Hl{|HV8hC+S)g`TvmbB#wDhPQF$z+dE z&yY|}s4WHR$_wZ8PYL^wZFnJ?4Efa}G$hfIT|Qo9m6kzeU&k@>EuCJCCGP%#=Egie z@8_6x*-XlvF-jYwb??J<9hn3KrerZs^p4u;wv zs!E0sf27sdCb|Rx{LT#sJhTx#zQ`2R69E9U7ya*o&1Zu0{R{akd2oS}A_f5He#0&# z>>Lxb`oECB0^dsjz~M!#Lj4D_^kOu?P0ZUT(9OYHGyv`PpUc5she9v-FJbRM+4%oO z`9Go01t0Ve$}DUql=I)vU+)+a0HA>t0J!`Ilp2eNvi<}8?Hp4VCjG1ie?Y%sxtK*U z|GxwK+lGHwe>y|xClAMUQ>Y pOn$M2L=0GJ1~#GpL|2|qN6Y^F9~Y26?PSApvG9||F#VkSzW}gN_22*i diff --git a/ClosedXML.Tests/Resource/Examples/ConditionalFormatting/CFDataBars.xlsx b/ClosedXML.Tests/Resource/Examples/ConditionalFormatting/CFDataBars.xlsx index 236b6e30e20ea7718dd6e8853c57e5be8a200932..24ed742f538b1fcc2249541212a65debac3e6fd4 100644 GIT binary patch delta 5282 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 ND6;SnW-43BO-(vdI=pAArL@{fD{QT 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 t49N9&twHPF#HjgNE7U%Dg{{i-2NzVWP diff --git a/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverterExtension.cs b/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverterExtension.cs index 0ea59f6..20f90c5 100644 --- a/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverterExtension.cs +++ b/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverterExtension.cs @@ -2,7 +2,6 @@ using ClosedXML.Extensions; using DocumentFormat.OpenXml.Office.Excel; using DocumentFormat.OpenXml.Office2010.Excel; using System.Collections.Generic; -using System.Linq; namespace ClosedXML.Excel { @@ -11,8 +10,8 @@ namespace ClosedXML.Excel private static readonly IReadOnlyDictionary CFValueToTypeMap = new Dictionary { - { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Max, ConditionalFormattingValueObjectTypeValues.AutoMax }, - { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Min, ConditionalFormattingValueObjectTypeValues.AutoMin }, + { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Max, ConditionalFormattingValueObjectTypeValues.Max }, + { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Min, ConditionalFormattingValueObjectTypeValues.Min }, { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Number, ConditionalFormattingValueObjectTypeValues.Numeric }, { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Percent, ConditionalFormattingValueObjectTypeValues.Percent }, { DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Percentile, ConditionalFormattingValueObjectTypeValues.Percentile }, @@ -35,25 +34,8 @@ namespace ClosedXML.Excel ShowValue = !cf.ShowBarOnly }; - var cfMinType = cf.ContentTypes.TryGetValue(1, out var contentType1) - ? GetCFType(contentType1.ToOpenXml()) - : ConditionalFormattingValueObjectTypeValues.AutoMin; - var cfMin = new ConditionalFormattingValueObject { Type = cfMinType }; - if (cf.Values.Any() && cf.Values[1]?.Value != null) - { - cfMin.Type = ConditionalFormattingValueObjectTypeValues.Numeric; - cfMin.Append(new Formula() { Text = cf.Values[1].Value }); - } - - var cfMaxType = cf.ContentTypes.TryGetValue(2, out var contentType2) - ? GetCFType(contentType2.ToOpenXml()) - : ConditionalFormattingValueObjectTypeValues.AutoMax; - var cfMax = new ConditionalFormattingValueObject { Type = cfMaxType }; - if (cf.Values.Count >= 2 && cf.Values[2]?.Value != null) - { - cfMax.Type = ConditionalFormattingValueObjectTypeValues.Numeric; - cfMax.Append(new Formula() { Text = cf.Values[2].Value }); - } + var cfMin = BuildValueObject(cf, 1, ConditionalFormattingValueObjectTypeValues.AutoMin); + var cfMax = BuildValueObject(cf, 2, ConditionalFormattingValueObjectTypeValues.AutoMax); var barAxisColor = new BarAxisColor { Rgb = XLColor.Black.Color.ToHex() }; @@ -74,6 +56,55 @@ namespace ClosedXML.Excel return conditionalFormattingRule; } + /// + /// Builds a single x14 conditional formatting value object (cfvo) for a data bar threshold. + /// + /// The conditional format the data bar belongs to. + /// 1-based threshold index (1 = minimum, 2 = maximum). + /// + /// Type emitted for an automatic bound: autoMin for the minimum, autoMax for the maximum. + /// + /// + /// A threshold without an explicit content type is an automatic bound (autoMin/autoMax). + /// A min/max threshold is the lowest/highest value; both it and the automatic bound are + /// self-describing and carry no formula. Only value based thresholds (num/percent/percentile/ + /// formula) emit an xm:f formula. Writing a formula for a min/max threshold makes strict + /// readers such as WPS lock the bar to that value (see issue #2843). + /// + private static ConditionalFormattingValueObject BuildValueObject(IXLConditionalFormat cf, int index, ConditionalFormattingValueObjectTypeValues automaticType) + { + cf.Values.TryGetValue(index, out var value); + var formulaText = value?.Value; + + if (!cf.ContentTypes.TryGetValue(index, out var contentType)) + { + return new ConditionalFormattingValueObject { Type = automaticType }; + } + + var type = GetCFType(contentType.ToOpenXml()); + var valueObject = new ConditionalFormattingValueObject { Type = type }; + + if (type == ConditionalFormattingValueObjectTypeValues.Min || + type == ConditionalFormattingValueObjectTypeValues.Max) + { + // A min/max threshold with no value is an automatic bound (autoMin/autoMax); + // with a value it is the lowest/highest value. Neither carries a formula. + if (formulaText == null) + { + valueObject.Type = automaticType; + } + + return valueObject; + } + + if (formulaText != null) + { + valueObject.Append(new Formula { Text = formulaText }); + } + + return valueObject; + } + private static ConditionalFormattingValueObjectTypeValues GetCFType(DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues value) { return CFValueToTypeMap[value];