From e6d658ab508cf6a1c243f375d2321e853a69061d Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 27 May 2026 01:39:41 +0300 Subject: [PATCH 1/2] Add support for custom list bullets --- examples/Demo/Program.cs | 2 +- examples/Demo/Resources/UlStyles.html | 31 ++++++++++++ .../Expressions/Numbering/ListExpression.cs | 10 +++- .../Numbering/NumberingExpressionBase.cs | 48 ++++++++++++++++++- 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 examples/Demo/Resources/UlStyles.html diff --git a/examples/Demo/Program.cs b/examples/Demo/Program.cs index d1ee82e..fccd469 100644 --- a/examples/Demo/Program.cs +++ b/examples/Demo/Program.cs @@ -12,7 +12,7 @@ static class Program static async Task Main(string[] args) { const string filename = "test.docx"; - string html = ResourceHelper.GetString("Resources.CompleteRunTest.html"); + string html = ResourceHelper.GetString("Resources.UlStyles.html"); if (File.Exists(filename)) File.Delete(filename); using (MemoryStream generatedDocument = new()) diff --git a/examples/Demo/Resources/UlStyles.html b/examples/Demo/Resources/UlStyles.html new file mode 100644 index 0000000..fc72b27 --- /dev/null +++ b/examples/Demo/Resources/UlStyles.html @@ -0,0 +1,31 @@ + + + + + + + +

 

+ +

 

+ +

 

+ + + \ No newline at end of file diff --git a/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs b/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs index 9ce33e3..0056d1b 100644 --- a/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs +++ b/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs @@ -227,7 +227,15 @@ private static string GetListName(IElement listNode, string? parentName = null) if (parentName != null && IsCascadingStyle(parentName)) return parentName!; - type = orderedList? "decimal" : "disc"; + // If a specific list-style-type is provided for an unordered list (e.g., a custom string like "'-'"), + // we strip the surrounding quotes to extract the raw symbol. + // Otherwise, we fallback to the default "decimal" or "disc" styles. + if (!orderedList && !string.IsNullOrEmpty(type)) + { + return type!.Trim('\'', '\"'); + } + + return orderedList ? "decimal" : "disc"; } return type!; diff --git a/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs b/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs index b41c4ee..23a6719 100644 --- a/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs +++ b/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs @@ -50,8 +50,20 @@ protected int GetOrCreateListTemplate(ParsingContext context, string listName) Numbering numberingPart = context.MainPart.NumberingDefinitionsPart!.Numbering!; + AbstractNum abstractNum; + // at this stage, we have sanitized the list style so it's safe to grab them from the predefined template lists - var abstractNum = predefinedNumberingLists[listName]; + if (predefinedNumberingLists.TryGetValue(listName, out var predefined)) + { + // If it's a predefined style, we clone it as usual + abstractNum = (AbstractNum)predefined.CloneNode(true); + } + else + { + // If not found in predefined lists, listName contains a custom bullet character (e.g., "-") + abstractNum = CreateCustomBulletAbstractNum(listName); + } + abstractNum = (AbstractNum) abstractNum.CloneNode(true); abstractNum.AbstractNumberId = IncrementAbstractNumId(context, numberingPart); var level1 = abstractNum.GetFirstChild()!; @@ -216,6 +228,38 @@ private void InitNumberingIds(ParsingContext context) isInitialized = true; } + /// + /// Generates a custom abstract numbering definition for unordered lists using a specific symbol. + /// + /// The custom character or string to be used as the list bullet. + /// An instance configured with the custom bullet symbol across all levels. + private AbstractNum CreateCustomBulletAbstractNum(string customSymbol) + { + var abstractNum = new AbstractNum { + AbstractNumDefinitionName = new() { Val = customSymbol }, + MultiLevelType = new() { Val = MultiLevelValues.HybridMultilevel } + }; + + for (var lvlIndex = 0; lvlIndex <= MaxLevel; lvlIndex++) + { + abstractNum.Append(new Level { + StartNumberingValue = new() { Val = 1 }, + NumberingFormat = new() { Val = NumberFormatValues.Bullet }, + LevelIndex = lvlIndex, + LevelText = new() { Val = string.Format(customSymbol, lvlIndex+1) }, + LevelJustification = new() { Val = LevelJustificationValues.Left }, + PreviousParagraphProperties = new() { + Indentation = new() { + Left = ((lvlIndex + 1) * Indentation * 2).ToString(), + Hanging = Indentation.ToString() + } + } + }); + } + + return abstractNum; + } + /// /// Predefined template of lists. /// @@ -299,4 +343,4 @@ private static IReadOnlyDictionary InitKnownLists() return knownAbstractNums; #endif } -} \ No newline at end of file +} From f0924cf896f0503c67eb9501bcd0c92466a459f1 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 27 May 2026 20:27:19 +0300 Subject: [PATCH 2/2] Add dash to supportedListTypes --- examples/Demo/Resources/UlStyles.html | 9 ++++++++- src/Html2OpenXml/Expressions/Numbering/ListExpression.cs | 2 +- .../Expressions/Numbering/NumberingExpressionBase.cs | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/Demo/Resources/UlStyles.html b/examples/Demo/Resources/UlStyles.html index fc72b27..bfe2fc6 100644 --- a/examples/Demo/Resources/UlStyles.html +++ b/examples/Demo/Resources/UlStyles.html @@ -10,7 +10,7 @@
  •  
  •  

    -
      +
      • test 2
      • test 3
      •  
      • @@ -27,5 +27,12 @@
      • test 3
      •  
      + +

       

      +
        +
      • test 2
      • +
      • test 3
      • +
      •  
      • +
      \ No newline at end of file diff --git a/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs b/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs index 0056d1b..199d0ae 100644 --- a/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs +++ b/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs @@ -38,7 +38,7 @@ readonly struct ListContext(string listName, int absNumId, int instanceId, int l // https://answers.microsoft.com/en-us/msoffice/forum/all/custom-list-number-style/21a54399-4404-4c37-8843-2ccaaf827485 // Image bullet: http://officeopenxml.com/WPnumbering-imagesAsSymbol.php private static readonly HashSet supportedListTypes = - ["disc", "decimal", "square", "circle", + ["disc", "decimal", "square", "circle", "dash", "lower-alpha", "upper-alpha", "lower-latin", "upper-latin", "lower-greek", "upper-greek", "lower-roman", "upper-roman", diff --git a/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs b/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs index 23a6719..95c953c 100644 --- a/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs +++ b/src/Html2OpenXml/Expressions/Numbering/NumberingExpressionBase.cs @@ -274,6 +274,7 @@ private static IReadOnlyDictionary InitKnownLists() ("disc", NumberFormatValues.Bullet, "•"), ("square", NumberFormatValues.Bullet, "▪"), ("circle", NumberFormatValues.Bullet, "o"), + ("dash", NumberFormatValues.Bullet, "-"), ("upper-alpha", NumberFormatValues.UpperLetter, "%{0}."), ("lower-alpha", NumberFormatValues.LowerLetter, "%{0}."), ("upper-roman", NumberFormatValues.UpperRoman, "%{0}."),