From b9ddec872ec57d604a696f23c95d80e20f9e5c78 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 10 Jul 2026 13:56:32 -0400 Subject: [PATCH 1/4] Fix LT-22351: load default font features in BaseStyleInfo.ProcessStyleRules BaseStyleInfo.ProcessStyleRules iterates the string properties of a style's TsTextProps and handles ktptFontFamily, ktptBulNumTxtBef/Aft, ktptCustomBullet, ktptBulNumFontInfo, and ktptWsStyle, but had no case for ktptFontVariations (font features). As a result, font features set as a style-level default (not a per-writing-system override) fell through to the default: break and were silently dropped when the style was loaded/reloaded. The save path already writes ktptFontVariations for style defaults (see GetOverridesString), and SetFontStringProp already knows how to apply it to a FontInfo -- it's just never called for the default font info, only for per-WS overrides via MakeFontWsOverrides. This created a save/load asymmetry: a user could set font features as a style default via the Styles dialog, save, and have them vanish on reload. Fix: add a case for ktptFontVariations in the string-prop switch in ProcessStyleRules that sets m_defaultFontInfo.m_features.ExplicitValue, mirroring how ktptFontFamily sets m_fontName. Added BaseStyleInfoTests.ConstructBasedOnStyle_FontFeatures, which builds style rules with ktptFontVariations set as a default (no WS override) and asserts FontInfoForWs(-1).m_features.IsExplicit is true with the value round-tripping exactly. Confirmed the test fails before the fix (IsExplicit false) and passes after, with no regressions in the broader DomainServices test suite (488 passed / 0 failed on both net462 and net8.0). Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + .../DomainServices/BaseStyleInfo.cs | 4 +++ .../DomainServices/BaseStyleInfoTests.cs | 28 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0850fb5a..53d84bce5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- [SIL.LCModel] BaseStyleInfo now loads default font features (ktptFontVariations) from style rules, fixing font features set as style defaults being dropped on reload (LT-22351) - [SIL.LCModel] Deleting a duplicated phonological rule no longer deletes pooled feature constraints and environment contexts the original rule still references (LT-22575) - [SIL.LCModel] Fixed crash when bulk deleting entries involved in a lexical relation (LT-21598) - [SIL.LCModel.FixData] Find and Fix removes duplicate Targets from a LexReference (and deletes it if fewer than two distinct Targets remain) (LT-21598) diff --git a/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs b/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs index 8bb240982..04e377168 100644 --- a/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs +++ b/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs @@ -935,6 +935,10 @@ private void ProcessStyleRules(ITsTextProps styleProps, m_defaultFontInfo.m_fontName.ExplicitValue = sProp; break; + case (int)FwTextPropType.ktptFontVariations: + m_defaultFontInfo.m_features.ExplicitValue = sProp; + break; + case (int)FwTextPropType.ktptBulNumTxtBef: { m_bulletInfo.IsInherited = false; diff --git a/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs b/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs index 87ba5b6a9..fabc6ba75 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs @@ -274,6 +274,34 @@ public void CreateCopy() Assert.AreEqual(false, newInfo.IsBuiltIn, "Copies of styles should not be considered built in"); } + /// ------------------------------------------------------------------------------------ + /// + /// Tests that font features (ktptFontVariations) set as a style-level default (not a + /// writing-system override) are loaded into the default font info. See LT-22351: such + /// features were saved to the database but silently dropped when the style was reloaded, + /// because ProcessStyleRules had no case for ktptFontVariations. + /// + /// ------------------------------------------------------------------------------------ + [Test] + public void ConstructBasedOnStyle_FontFeatures() + { + ITsPropsBldr props; + + IStStyle mainTitleStyle = AddTestStyle("Title Main", ContextValues.Title, + StructureValues.Body, FunctionValues.Prose, false, Cache.LangProject.StylesOC); + props = mainTitleStyle.Rules.GetBldr(); + props.SetStrPropValue((int)FwTextPropType.ktptFontFamily, "Arial"); + props.SetStrPropValue((int)FwTextPropType.ktptFontVariations, "smcp=1,ss01=2"); + mainTitleStyle.Rules = props.GetTextProps(); + + DummyStyleInfo entry = new DummyStyleInfo(mainTitleStyle); + FontInfo fontInfo = entry.FontInfoForWs(-1); + Assert.IsNotNull(fontInfo); + Assert.IsTrue(fontInfo.m_features.IsExplicit, + "Default font features set on the style should be explicit, not inherited"); + Assert.AreEqual("smcp=1,ss01=2", fontInfo.m_features.Value); + } + /// /// Minimal test of an alternate constructor to verify that it records the cache from the style. /// From f016025b13e59e0d9bf8e20b5981809323e94d1f Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 10 Jul 2026 15:36:51 -0400 Subject: [PATCH 2/4] Fix LT-22351: don't clobber ws font overrides with default font values Review follow-ups on the ktptFontVariations load fix: Loading font features into the default font info made FontInfo.IsAnyExplicit true for styles whose only explicit style-level property is font features. That newly triggered the branch in SetNonExplicitPropertiesToInherited that pushes the default font info into each ws-specific override via InheritAllProperties -- AFTER the override had already inherited the based-on style's corresponding ws override. Since InheritValue overwrites the value of every still-inherited property (last caller wins), the second pass clobbered what the first pass set: e.g. a based-on style with ws override FontName=Fontastic for English but style-level default Times New Roman would flip a child style's English override font to Times New Roman when the child's only explicit default was font features. Fix: add FontInfo.InheritExplicitProperties, which propagates only the explicitly-set properties of the source font info, and use it for the default-into-override pass. The default's other values were merely inherited from the based-on style's default and must not overwrite values just inherited from the based-on style's ws override. Note this intentionally changes long-standing behavior for styles with any explicit default font property (e.g. an explicit default font name): previously ALL still-inherited override properties were overwritten with the default font info's values; now only the explicitly-set default properties are propagated. The old behavior was the same clobber bug reached an older way. Also per review: - ProcessStyleRules now delegates the ktptFontVariations case to SetFontStringProp, keeping the tag-to-field mapping in one place (mirrors how the int-prop loop delegates to SetExplicitFontIntProp). - ConstructBasedOnStyle_FontFeatures now asserts the font family it sets up, matching the sibling ConstructBasedOnStyle test. Added SetBasedOnStyleAndInheritValues_ExplicitDefaultFeaturesDoNot- ClobberWsOverride, which fails before the fix (English override font "Times New Roman" instead of "Fontastic") and passes after. Full solution test suite is green on net462 and net8.0 (SIL.LCModel.Tests 1704 passed, Core.Tests 787, Utils.Tests 302, FixData.Tests 21). Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + .../DomainServices/BaseStyleInfo.cs | 8 ++- src/SIL.LCModel/DomainServices/FontInfo.cs | 35 +++++++++++ .../DomainServices/BaseStyleInfoTests.cs | 61 +++++++++++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53d84bce5..f931e1056 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - [SIL.LCModel] BaseStyleInfo now loads default font features (ktptFontVariations) from style rules, fixing font features set as style defaults being dropped on reload (LT-22351) +- [SIL.LCModel] BaseStyleInfo no longer clobbers ws-specific font overrides inherited from a based-on style with the default font info's merely-inherited values; only explicitly-set default properties are propagated to ws overrides (LT-22351) - [SIL.LCModel] Deleting a duplicated phonological rule no longer deletes pooled feature constraints and environment contexts the original rule still references (LT-22575) - [SIL.LCModel] Fixed crash when bulk deleting entries involved in a lexical relation (LT-21598) - [SIL.LCModel.FixData] Find and Fix removes duplicate Targets from a LexReference (and deletes it if fewer than two distinct Targets remain) (LT-21598) diff --git a/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs b/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs index 04e377168..abd3633ec 100644 --- a/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs +++ b/src/SIL.LCModel/DomainServices/BaseStyleInfo.cs @@ -936,7 +936,7 @@ private void ProcessStyleRules(ITsTextProps styleProps, break; case (int)FwTextPropType.ktptFontVariations: - m_defaultFontInfo.m_features.ExplicitValue = sProp; + SetFontStringProp(tpt, m_defaultFontInfo, sProp); break; case (int)FwTextPropType.ktptBulNumTxtBef: @@ -1440,7 +1440,11 @@ private void SetNonExplicitPropertiesToInherited() fontInfoOverride.Value.InheritAllProperties(inheritPropsFrom); if (m_defaultFontInfo.IsAnyExplicit) { - fontInfoOverride.Value.InheritAllProperties(m_defaultFontInfo); + // Propagate only the explicitly-set default (style-level) properties into the + // override; the default's other values were merely inherited from the based-on + // style's default and must not clobber what was just inherited from the + // based-on style's ws override. + fontInfoOverride.Value.InheritExplicitProperties(m_defaultFontInfo); } } m_rtl.InheritValue(basedOn.m_rtl); diff --git a/src/SIL.LCModel/DomainServices/FontInfo.cs b/src/SIL.LCModel/DomainServices/FontInfo.cs index 474d19861..b8f49bfae 100644 --- a/src/SIL.LCModel/DomainServices/FontInfo.cs +++ b/src/SIL.LCModel/DomainServices/FontInfo.cs @@ -273,5 +273,40 @@ internal void InheritAllProperties(FontInfo basedOnFontInfo) m_offset.InheritValue(basedOnFontInfo.m_offset); m_features.InheritValue(basedOnFontInfo.m_features); } + + /// ------------------------------------------------------------------------------------ + /// + /// Inherit the values of only those properties that are explicitly set on the specified + /// font info, but don't force a change in the inherit value. + /// + /// Explicitly set values on this font info will not be affected + /// The font info from which to get the explicitly set + /// values. + /// ------------------------------------------------------------------------------------ + internal void InheritExplicitProperties(FontInfo basedOnFontInfo) + { + if (basedOnFontInfo.m_fontName.IsExplicit) + m_fontName.InheritValue(basedOnFontInfo.m_fontName); + if (basedOnFontInfo.m_fontSize.IsExplicit) + m_fontSize.InheritValue(basedOnFontInfo.m_fontSize); + if (basedOnFontInfo.m_fontColor.IsExplicit) + m_fontColor.InheritValue(basedOnFontInfo.m_fontColor); + if (basedOnFontInfo.m_backColor.IsExplicit) + m_backColor.InheritValue(basedOnFontInfo.m_backColor); + if (basedOnFontInfo.m_bold.IsExplicit) + m_bold.InheritValue(basedOnFontInfo.m_bold); + if (basedOnFontInfo.m_italic.IsExplicit) + m_italic.InheritValue(basedOnFontInfo.m_italic); + if (basedOnFontInfo.m_superSub.IsExplicit) + m_superSub.InheritValue(basedOnFontInfo.m_superSub); + if (basedOnFontInfo.m_underline.IsExplicit) + m_underline.InheritValue(basedOnFontInfo.m_underline); + if (basedOnFontInfo.m_underlineColor.IsExplicit) + m_underlineColor.InheritValue(basedOnFontInfo.m_underlineColor); + if (basedOnFontInfo.m_offset.IsExplicit) + m_offset.InheritValue(basedOnFontInfo.m_offset); + if (basedOnFontInfo.m_features.IsExplicit) + m_features.InheritValue(basedOnFontInfo.m_features); + } } } diff --git a/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs b/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs index fabc6ba75..e77bf2440 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs @@ -300,6 +300,7 @@ public void ConstructBasedOnStyle_FontFeatures() Assert.IsTrue(fontInfo.m_features.IsExplicit, "Default font features set on the style should be explicit, not inherited"); Assert.AreEqual("smcp=1,ss01=2", fontInfo.m_features.Value); + Assert.AreEqual("Arial", (string)fontInfo.m_fontName.Value); } /// @@ -430,6 +431,66 @@ public void SetBasedOnStyleAndInheritValues_WsOverrideInChildInheritsParentValue "The child entry in the StyleInfoCollection did not inherit the fontName properly"); } + /// + /// Tests that a child style whose only explicit style-level default is font features + /// (ktptFontVariations) does not clobber the ws-specific overrides it inherits from its + /// based-on style. The explicitly-set default properties should be propagated to the ws + /// overrides, but the default's merely-inherited values must not overwrite what was just + /// inherited from the based-on style's ws override. See LT-22351. + /// + [Test] + public void SetBasedOnStyleAndInheritValues_ExplicitDefaultFeaturesDoNotClobberWsOverride() + { + // Font family override for the parent style + byte[] buffer = new byte[] { + 0xC1, 0x87, 0x8B, 0x3B, // WS for english (little endian) + 0x09, 0x00, // FF length + (byte)'F', 0, + (byte)'o', 0, + (byte)'n', 0, + (byte)'t', 0, + (byte)'a', 0, + (byte)'s', 0, + (byte)'t', 0, + (byte)'i', 0, + (byte)'c', 0, + + 0x00, 0x00, // Count of int props + }; + + ITsPropsBldr props; + + var mainTitleStyle = AddTestStyle("Title Main", ContextValues.Title, + StructureValues.Body, FunctionValues.Prose, false, Cache.LangProject.StylesOC); + var inheritFromMain = AddTestStyle("Inherit Title", ContextValues.Title, StructureValues.Body, + FunctionValues.Prose, false, Cache.LangProject.StylesOC); + inheritFromMain.BasedOnRA = mainTitleStyle; + // The parent style has a ws-specific font override and a different default font + props = mainTitleStyle.Rules.GetBldr(); + props.SetStrPropValue((int)FwTextPropType.ktptFontFamily, "Times New Roman"); + props.SetStrPropValue((int)FwTextPropType.ktptWsStyle, DummyStyleInfo.MakeStringFromBuffer(buffer)); + mainTitleStyle.Rules = props.GetTextProps(); + // The child style's only explicit style-level default is font features + props = inheritFromMain.Rules.GetBldr(); + props.SetStrPropValue((int)FwTextPropType.ktptFontVariations, "smcp=1"); + inheritFromMain.Rules = props.GetTextProps(); + + var entry = new DummyStyleInfo(mainTitleStyle); + var childEntry = new DummyStyleInfo(inheritFromMain); + var styleInfos = new LcmStyleSheet.StyleInfoCollection(); + styleInfos.Add(entry); + styleInfos.Add(childEntry); + // SUT + childEntry.SetBasedOnStyleAndInheritValues(styleInfos); + var childOverrides = childEntry.FontInfoForWs(Cache.DefaultAnalWs); + Assert.True(childOverrides.m_fontName.IsInherited, + "The child entry's ws override should still inherit the fontName"); + Assert.AreEqual("Fontastic", childOverrides.m_fontName.Value, + "The fontName inherited from the parent's ws override should not be clobbered by the child's default font info"); + Assert.AreEqual("smcp=1", childOverrides.m_features.Value, + "The child's explicit default font features should be propagated to its ws override"); + } + /// ------------------------------------------------------------------------------------ /// /// Tests retrieving WS specific overrides from string From 791b76d55e4077afe2afe0a3ad8a6b3cc2d61025 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 10 Jul 2026 15:37:16 -0400 Subject: [PATCH 3/4] Fix LT-22351: decode all bullet font info props, not just the first string prop In BulletInfo.DecodeFontInfo, the ktptFontFamily and ktptFontVariations branches ended with loop-level break statements, so decoding stopped entirely at the first string property encountered. EncodeFontInfo always writes the font family before the font variations, which meant a bullet font with both a font name and font features lost its features on every decode -- another save/load asymmetry for ktptFontVariations. Fix: continue the loop after handling each string property instead of breaking out of it. The manual index advancement past the property's terminating NUL already leaves the index at the next property, so the int-prop parsing below is unaffected. Added BulletInfoTests.RoundTripEncodingAndDecodingOfFontInfo_FontName- AndFontFeatures, an encode/decode round trip with both m_fontName and m_features explicit. It fails before the fix (features not explicit after decode) and passes after, along with the rest of BulletInfoTests and the full solution test suite on net462 and net8.0. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + src/SIL.LCModel/DomainServices/BulletInfo.cs | 4 +-- .../DomainServices/BulletInfoTests.cs | 31 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f931e1056..44cc3c704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [SIL.LCModel] BaseStyleInfo now loads default font features (ktptFontVariations) from style rules, fixing font features set as style defaults being dropped on reload (LT-22351) - [SIL.LCModel] BaseStyleInfo no longer clobbers ws-specific font overrides inherited from a based-on style with the default font info's merely-inherited values; only explicitly-set default properties are propagated to ws overrides (LT-22351) +- [SIL.LCModel] BulletInfo now decodes all properties of an encoded bullet font info instead of stopping at the first string property, fixing bullet font features (ktptFontVariations) being lost when a bullet font name was also set (LT-22351) - [SIL.LCModel] Deleting a duplicated phonological rule no longer deletes pooled feature constraints and environment contexts the original rule still references (LT-22575) - [SIL.LCModel] Fixed crash when bulk deleting entries involved in a lexical relation (LT-21598) - [SIL.LCModel.FixData] Find and Fix removes duplicate Targets from a LexReference (and deletes it if fewer than two distinct Targets remain) (LT-21598) diff --git a/src/SIL.LCModel/DomainServices/BulletInfo.cs b/src/SIL.LCModel/DomainServices/BulletInfo.cs index f882f1f8f..7fc041494 100644 --- a/src/SIL.LCModel/DomainServices/BulletInfo.cs +++ b/src/SIL.LCModel/DomainServices/BulletInfo.cs @@ -210,13 +210,13 @@ private static FontInfo DecodeFontInfo(string blob) { fontInfo.m_fontName.ExplicitValue = blob.Substring(i, iPropLim - i); i += (iPropLim - i + 1); - break; + continue; } else if (tpt == FwTextPropType.ktptFontVariations) { fontInfo.m_features.ExplicitValue = blob.Substring(i, iPropLim - i); i += (iPropLim - i + 1); - break; + continue; } int nVal = (int)blob[i] + (int)(blob[i + 1] << 16); diff --git a/tests/SIL.LCModel.Tests/DomainServices/BulletInfoTests.cs b/tests/SIL.LCModel.Tests/DomainServices/BulletInfoTests.cs index 71d9a90df..fce53efa8 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/BulletInfoTests.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/BulletInfoTests.cs @@ -139,6 +139,37 @@ public void RoundTripEncodingAndDecodingOfFontInfo() Assert.AreEqual(expectedFontInfo, bulletInfo2.FontInfo); } + + /// ------------------------------------------------------------------------------------ + /// + /// Tests that we can save and restore a font info that has both a font name and font + /// features (ktptFontVariations) explicitly set. DecodeFontInfo used to stop parsing + /// after the first string property, so the font features (which are encoded after the + /// font name) were lost on every decode. See LT-22351. + /// + /// ------------------------------------------------------------------------------------ + [Test] + public void RoundTripEncodingAndDecodingOfFontInfo_FontNameAndFontFeatures() + { + // Setup what we expect + FontInfo expectedFontInfo = m_infoTable["TestStyle"].FontInfoForWs(-1); + expectedFontInfo.m_fontName = new InheritableStyleProp("Algerian"); + expectedFontInfo.m_features = new InheritableStyleProp("smcp=1,ss01=2"); + + BulletInfo bulletInfo1 = new BulletInfo(); + bulletInfo1.FontInfo = expectedFontInfo; + + BulletInfo bulletInfo2 = new BulletInfo(); + bulletInfo2.EncodedFontInfo = bulletInfo1.EncodedFontInfo; + + Assert.IsTrue(bulletInfo2.FontInfo.m_fontName.IsExplicit, + "The font name should survive the round trip"); + Assert.AreEqual("Algerian", bulletInfo2.FontInfo.m_fontName.Value); + Assert.IsTrue(bulletInfo2.FontInfo.m_features.IsExplicit, + "The font features should survive the round trip"); + Assert.AreEqual("smcp=1,ss01=2", bulletInfo2.FontInfo.m_features.Value); + Assert.AreEqual(expectedFontInfo, bulletInfo2.FontInfo); + } #endregion } } From 66eda1b71dc60f8c14e475e7ef05857e43be96a6 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 10 Jul 2026 17:34:01 -0400 Subject: [PATCH 4/4] Add tests pinning LT-18109 ws-override inheritance semantics Pin the intended behavior of the IsAnyExplicit block that commit 113a7685 (Fix for LT-18109 - Inherit writing system overrides, 2017) added to SetNonExplicitPropertiesToInherited, proving that the InheritExplicitProperties change preserves it: - SetBasedOnStyleAndInheritValues_ExplicitDefaultPropsPropagateInto- WsOverride: a child style's explicit style-level default (bold) shows through in its ws override, while the font name the child does not set explicitly keeps the value inherited from the parent's ws override. - SetBasedOnStyleAndInheritValues_ExplicitDefaultFontNameBeatsParent- WsOverride: a child style's explicit style-level default font name applies across writing systems and wins over the parent's ws override, per the LT-18109 commit message ("If the default font info in a style specifies overrides the defaults overrule the parent style ws overrides"). Together with SetBasedOnStyleAndInheritValues_ExplicitDefaultFeatures- DoNotClobberWsOverride these pin all three semantics: no clobber of inherited ws-override values by merely-inherited defaults, propagation of explicit defaults into ws overrides, and explicit-default precedence over the parent's ws override. Both new tests pass with no production change; full solution suite green on net462 and net8.0 (SIL.LCModel.Tests 1706 passed / 0 failed / 18 skipped, Core.Tests 787/787, Utils.Tests 302 passed / 2 skipped, FixData.Tests 21/21). Co-Authored-By: Claude Fable 5 --- .../DomainServices/BaseStyleInfoTests.cs | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs b/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs index e77bf2440..b7de9e3ef 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/BaseStyleInfoTests.cs @@ -491,6 +491,118 @@ public void SetBasedOnStyleAndInheritValues_ExplicitDefaultFeaturesDoNotClobberW "The child's explicit default font features should be propagated to its ws override"); } + /// + /// Tests that a child style's explicit style-level default properties are still + /// propagated into its ws-specific overrides (the intended behavior of LT-18109), + /// while properties the child does not set explicitly keep the values inherited from + /// the based-on style's ws override. See LT-22351. + /// + [Test] + public void SetBasedOnStyleAndInheritValues_ExplicitDefaultPropsPropagateIntoWsOverride() + { + // Font family override for the parent style + byte[] buffer = new byte[] { + 0xC1, 0x87, 0x8B, 0x3B, // WS for english (little endian) + 0x09, 0x00, // FF length + (byte)'F', 0, + (byte)'o', 0, + (byte)'n', 0, + (byte)'t', 0, + (byte)'a', 0, + (byte)'s', 0, + (byte)'t', 0, + (byte)'i', 0, + (byte)'c', 0, + + 0x00, 0x00, // Count of int props + }; + + ITsPropsBldr props; + + var mainTitleStyle = AddTestStyle("Title Main", ContextValues.Title, + StructureValues.Body, FunctionValues.Prose, false, Cache.LangProject.StylesOC); + var inheritFromMain = AddTestStyle("Inherit Title", ContextValues.Title, StructureValues.Body, + FunctionValues.Prose, false, Cache.LangProject.StylesOC); + inheritFromMain.BasedOnRA = mainTitleStyle; + // The parent style has a ws-specific font override and a different default font + props = mainTitleStyle.Rules.GetBldr(); + props.SetStrPropValue((int)FwTextPropType.ktptFontFamily, "Times New Roman"); + props.SetStrPropValue((int)FwTextPropType.ktptWsStyle, DummyStyleInfo.MakeStringFromBuffer(buffer)); + mainTitleStyle.Rules = props.GetTextProps(); + // The child style's only explicit style-level default is bold, which the parent's + // ws override does not set + props = inheritFromMain.Rules.GetBldr(); + props.SetIntPropValues((int)FwTextPropType.ktptBold, + (int)FwTextPropVar.ktpvDefault, 1); + inheritFromMain.Rules = props.GetTextProps(); + + var entry = new DummyStyleInfo(mainTitleStyle); + var childEntry = new DummyStyleInfo(inheritFromMain); + var styleInfos = new LcmStyleSheet.StyleInfoCollection(); + styleInfos.Add(entry); + styleInfos.Add(childEntry); + // SUT + childEntry.SetBasedOnStyleAndInheritValues(styleInfos); + var childOverrides = childEntry.FontInfoForWs(Cache.DefaultAnalWs); + Assert.AreEqual("Fontastic", childOverrides.m_fontName.Value, + "The fontName inherited from the parent's ws override should not be clobbered by the child's default font info"); + Assert.IsTrue(childOverrides.m_bold.Value, + "The child's explicit default bold setting should be propagated to its ws override"); + } + + /// + /// Tests that a child style's explicit style-level default font name applies across + /// writing systems: it wins over the font name in the based-on style's ws override. + /// See LT-22351. + /// + [Test] + public void SetBasedOnStyleAndInheritValues_ExplicitDefaultFontNameBeatsParentWsOverride() + { + // Font family override for the parent style + byte[] buffer = new byte[] { + 0xC1, 0x87, 0x8B, 0x3B, // WS for english (little endian) + 0x09, 0x00, // FF length + (byte)'F', 0, + (byte)'o', 0, + (byte)'n', 0, + (byte)'t', 0, + (byte)'a', 0, + (byte)'s', 0, + (byte)'t', 0, + (byte)'i', 0, + (byte)'c', 0, + + 0x00, 0x00, // Count of int props + }; + + ITsPropsBldr props; + + var mainTitleStyle = AddTestStyle("Title Main", ContextValues.Title, + StructureValues.Body, FunctionValues.Prose, false, Cache.LangProject.StylesOC); + var inheritFromMain = AddTestStyle("Inherit Title", ContextValues.Title, StructureValues.Body, + FunctionValues.Prose, false, Cache.LangProject.StylesOC); + inheritFromMain.BasedOnRA = mainTitleStyle; + // The parent style has a ws-specific font override + props = mainTitleStyle.Rules.GetBldr(); + props.SetStrPropValue((int)FwTextPropType.ktptWsStyle, DummyStyleInfo.MakeStringFromBuffer(buffer)); + mainTitleStyle.Rules = props.GetTextProps(); + // The child style has an explicit style-level default font name + props = inheritFromMain.Rules.GetBldr(); + props.SetStrPropValue((int)FwTextPropType.ktptFontFamily, "Times New Roman"); + inheritFromMain.Rules = props.GetTextProps(); + + var entry = new DummyStyleInfo(mainTitleStyle); + var childEntry = new DummyStyleInfo(inheritFromMain); + var styleInfos = new LcmStyleSheet.StyleInfoCollection(); + styleInfos.Add(entry); + styleInfos.Add(childEntry); + // SUT + childEntry.SetBasedOnStyleAndInheritValues(styleInfos); + var childOverrides = childEntry.FontInfoForWs(Cache.DefaultAnalWs); + Assert.AreEqual("Times New Roman", childOverrides.m_fontName.Value, + "The child's explicit default font name should apply across writing systems, winning over the parent's ws override"); + } + /// ------------------------------------------------------------------------------------ /// /// Tests retrieving WS specific overrides from string