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..bfe2fc6 --- /dev/null +++ b/examples/Demo/Resources/UlStyles.html @@ -0,0 +1,38 @@ + + + + + + + +

 

+ +

 

+ +

 

+ + +

 

+ + + \ No newline at end of file diff --git a/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs b/src/Html2OpenXml/Expressions/Numbering/ListExpression.cs index 9ce33e3..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", @@ -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..95c953c 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. /// @@ -230,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}."), @@ -299,4 +344,4 @@ private static IReadOnlyDictionary InitKnownLists() return knownAbstractNums; #endif } -} \ No newline at end of file +}