@@ -170,12 +170,20 @@ internal static DocxDocument Read(Stream stream)
170170 extentWidthPt = cx / 914400f * 72f ;
171171 }
172172
173- // Read text box outline (border) from shape properties
173+ // Read text box outline (border) and fill from shape properties
174174 DocxTextBoxBorder ? textBoxBorder = null ;
175+ PdfColor ? textBoxFillColor = null ;
175176 var wsp = anchor . Descendants ( WPS + "wsp" ) . FirstOrDefault ( ) ;
176177 if ( wsp != null )
177178 {
178179 var spPr = wsp . Element ( WPS + "spPr" ) ?? wsp . Element ( A + "spPr" ) ;
180+ // Parse shape fill (background)
181+ var shapeFill = spPr ? . Element ( A + "solidFill" ) ;
182+ if ( shapeFill != null )
183+ {
184+ var ( fc , _) = ResolveSolidFill ( shapeFill , themeColors ) ;
185+ textBoxFillColor = fc ;
186+ }
179187 var ln = spPr ? . Element ( A + "ln" ) ;
180188 if ( ln != null )
181189 {
@@ -242,7 +250,7 @@ internal static DocxDocument Read(Stream stream)
242250 if ( floatingParas . Count > 0 )
243251 {
244252 floatingTextBoxes ??= new List < DocxFloatingTextBox > ( ) ;
245- floatingTextBoxes . Add ( new DocxFloatingTextBox ( anchorXPt , anchorOffsetPt , extentWidthPt , extentHeightPt , floatingParas , textBoxBorder , hRelativeFrom , vRelativeFrom ) ) ;
253+ floatingTextBoxes . Add ( new DocxFloatingTextBox ( anchorXPt , anchorOffsetPt , extentWidthPt , extentHeightPt , floatingParas , textBoxBorder , hRelativeFrom , vRelativeFrom , textBoxFillColor ) ) ;
246254 }
247255 }
248256 else
@@ -490,7 +498,8 @@ internal static DocxDocument Read(Stream stream)
490498 if ( numDef . Format == "bullet" )
491499 {
492500 isBulletList = true ;
493- listText = "\u2022 " ; // bullet character
501+ var lvlDef2 = numDef . Levels . FirstOrDefault ( l => l . Ilvl == listLevel ) ?? numDef . Levels . FirstOrDefault ( ) ;
502+ listText = MapBulletChar ( lvlDef2 ? . LvlText , lvlDef2 ? . FontName ) ;
494503 }
495504 else
496505 {
@@ -2758,6 +2767,51 @@ private static (string? MajorLatinFont, string? MinorLatinFont, Dictionary<strin
27582767 return null ;
27592768 }
27602769
2770+ /// <summary>
2771+ /// Maps a bullet character from Wingdings/Symbol font encoding to a Unicode equivalent.
2772+ /// </summary>
2773+ private static string MapBulletChar ( string ? lvlText , string ? fontName )
2774+ {
2775+ if ( string . IsNullOrEmpty ( lvlText ) )
2776+ return "\u2022 " ; // fallback bullet
2777+
2778+ var ch = lvlText [ 0 ] ;
2779+
2780+ if ( fontName != null && fontName . Contains ( "Wingdings" , StringComparison . OrdinalIgnoreCase ) )
2781+ {
2782+ // Wingdings PUA → Unicode mappings (common bullets)
2783+ return ch switch
2784+ {
2785+ '\uf0d8 ' => "\u27A2 " , // ➢ right arrowhead
2786+ '\uf0a7 ' => "\u25AA " , // ▪ small black square
2787+ '\uf0a8 ' => "\u25CB " , // ○ white circle
2788+ '\uf076 ' => "\u2756 " , // ❖ black diamond minus white X
2789+ '\uf0FC ' => "\u2714 " , // ✔ check mark
2790+ '\uf0FB ' => "\u2718 " , // ✘ cross mark
2791+ '\uf0E8 ' => "\u25BA " , // ► right-pointing triangle
2792+ '\uf0D2 ' => "\u27A4 " , // ➤ right arrowhead (filled)
2793+ _ => "\u2022 " , // fallback
2794+ } ;
2795+ }
2796+
2797+ if ( fontName != null && fontName . Contains ( "Symbol" , StringComparison . OrdinalIgnoreCase ) )
2798+ {
2799+ return ch switch
2800+ {
2801+ '\uf0b7 ' => "\u2022 " , // • bullet
2802+ '\uf0a7 ' => "\u2666 " , // ♦ diamond
2803+ '\uf0B0 ' => "\u2218 " , // ∘ ring operator
2804+ _ => "\u2022 " ,
2805+ } ;
2806+ }
2807+
2808+ // For standard fonts, use the character as-is if printable
2809+ if ( ch >= ' ' )
2810+ return lvlText ;
2811+
2812+ return "\u2022 " ; // fallback
2813+ }
2814+
27612815 private static Dictionary < string , DocxNumberingDef > ReadNumbering ( ZipArchive archive )
27622816 {
27632817 var result = new Dictionary < string , DocxNumberingDef > ( ) ;
@@ -2791,7 +2845,9 @@ private static Dictionary<string, DocxNumberingDef> ReadNumbering(ZipArchive arc
27912845 if ( int . TryParse ( lvlInd . Attribute ( W + "hanging" ) ? . Value , out var lh ) )
27922846 lvlHanging = lh / 20f ;
27932847 }
2794- levels . Add ( new DocxNumberingLevelDef ( ilvl , numFmt , lvlText , startVal , lvlIndentLeft , lvlHanging ) ) ;
2848+ // Read bullet font name from rPr/rFonts (e.g. Wingdings, Symbol)
2849+ var lvlFontName = lvl . Element ( W + "rPr" ) ? . Element ( W + "rFonts" ) ? . Attribute ( W + "ascii" ) ? . Value ;
2850+ levels . Add ( new DocxNumberingLevelDef ( ilvl , numFmt , lvlText , startVal , lvlIndentLeft , lvlHanging , lvlFontName ) ) ;
27952851 }
27962852 abstractDefs [ absId ] = levels ;
27972853 }
@@ -2957,7 +3013,8 @@ internal sealed record DocxFloatingTextBox(
29573013 List < DocxParagraph > Paragraphs ,
29583014 DocxTextBoxBorder ? Border = null ,
29593015 string HRelativeFrom = "column" ,
2960- string VRelativeFrom = "paragraph"
3016+ string VRelativeFrom = "paragraph" ,
3017+ PdfColor ? FillColor = null
29613018) ;
29623019
29633020/// <summary>Represents a text box outline border (rectangle drawn around text box content).</summary>
@@ -3157,4 +3214,4 @@ private static string ToRoman(int num)
31573214 }
31583215}
31593216
3160- internal sealed record DocxNumberingLevelDef ( int Ilvl , string NumFmt , string LvlText , int Start , float IndentLeft = 0 , float Hanging = 0 ) ;
3217+ internal sealed record DocxNumberingLevelDef ( int Ilvl , string NumFmt , string LvlText , int Start , float IndentLeft = 0 , float Hanging = 0 , string ? FontName = null ) ;
0 commit comments