diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs
index 6e48862..33fed54 100644
--- a/src/MiniWord/MiniWord.Implment.cs
+++ b/src/MiniWord/MiniWord.Implment.cs
@@ -592,6 +592,13 @@ private static void ReplaceText(Paragraph p, WordprocessingDocument docx, Dictio
t.Remove();
}
+ else if (value is MiniWordTableBorder tableBorder)
+ {
+ var parentTable = p.Ancestors
().FirstOrDefault();
+ if (parentTable != null)
+ ApplyTableBorder(parentTable, tableBorder);
+ t.Text = t.Text.Replace($"{{{{{tag.Key}}}}}", "");
+ }
else if (value is IEnumerable pictures)
{
// 新增一个标签插入图片集合
@@ -1160,6 +1167,71 @@ private static void AddPictureAnchor(OpenXmlElement appendElement, string relati
appendElement.Append((drawing13));
}
+ private static void ApplyTableBorder(Table table, MiniWordTableBorder border)
+ {
+ var tableProperties = table.GetFirstChild();
+ if (tableProperties == null)
+ {
+ tableProperties = new TableProperties();
+ table.PrependChild(tableProperties);
+ }
+
+ tableProperties.GetFirstChild()?.Remove();
+
+ var tableBorders = new TableBorders();
+ if (border.TopBorder != null)
+ tableBorders.AppendChild(new TopBorder
+ {
+ Val = border.TopBorder.Style,
+ Color = border.TopBorder.Color?.Replace("#", ""),
+ Size = border.TopBorder.Size,
+ Space = border.TopBorder.Space
+ });
+ if (border.BottomBorder != null)
+ tableBorders.AppendChild(new BottomBorder
+ {
+ Val = border.BottomBorder.Style,
+ Color = border.BottomBorder.Color?.Replace("#", ""),
+ Size = border.BottomBorder.Size,
+ Space = border.BottomBorder.Space
+ });
+ if (border.LeftBorder != null)
+ tableBorders.AppendChild(new LeftBorder
+ {
+ Val = border.LeftBorder.Style,
+ Color = border.LeftBorder.Color?.Replace("#", ""),
+ Size = border.LeftBorder.Size,
+ Space = border.LeftBorder.Space
+ });
+ if (border.RightBorder != null)
+ tableBorders.AppendChild(new RightBorder
+ {
+ Val = border.RightBorder.Style,
+ Color = border.RightBorder.Color?.Replace("#", ""),
+ Size = border.RightBorder.Size,
+ Space = border.RightBorder.Space
+ });
+ if (border.InsideHorizontalBorder != null)
+ tableBorders.AppendChild(new InsideHorizontalBorder
+ {
+ Val = border.InsideHorizontalBorder.Style,
+ Color = border.InsideHorizontalBorder.Color?.Replace("#", ""),
+ Size = border.InsideHorizontalBorder.Size,
+ Space = border.InsideHorizontalBorder.Space
+ });
+ if (border.InsideVerticalBorder != null)
+ tableBorders.AppendChild(new InsideVerticalBorder
+ {
+ Val = border.InsideVerticalBorder.Style,
+ Color = border.InsideVerticalBorder.Color?.Replace("#", ""),
+ Size = border.InsideVerticalBorder.Size,
+ Space = border.InsideVerticalBorder.Space
+ });
+
+ if (tableBorders.HasChildren)
+ tableProperties.Append(tableBorders);
+ }
+
private static byte[] GetBytes(string path)
{
return GetByteAsync(path).GetAwaiter().GetResult();
diff --git a/src/MiniWord/MiniWordTableBorder.cs b/src/MiniWord/MiniWordTableBorder.cs
new file mode 100644
index 0000000..8192af8
--- /dev/null
+++ b/src/MiniWord/MiniWordTableBorder.cs
@@ -0,0 +1,82 @@
+using DocumentFormat.OpenXml.Wordprocessing;
+
+namespace MiniSoftware
+{
+ ///
+ /// Represents a single border edge (top, bottom, left, right, inside horizontal, inside vertical).
+ ///
+ public class MiniWordBorder
+ {
+ /// Border line style. Defaults to .
+ public BorderValues Style { get; set; } = BorderValues.Single;
+
+ /// Border color as a hex string, e.g. "000000" or "#000000".
+ public string Color { get; set; } = "000000";
+
+ /// Border width in eighths of a point. Defaults to 4 (= 0.5 pt).
+ public uint Size { get; set; } = 4;
+
+ /// Space between the border and text, in points. Defaults to 0.
+ public uint Space { get; set; } = 0;
+ }
+
+ ///
+ /// Specifies table-level border settings that MiniWord applies when the tag value is
+ /// a instance. Place {{tagName}} anywhere
+ /// inside a table cell; MiniWord will apply the borders to the parent table and clear
+ /// the placeholder text.
+ ///
+ public class MiniWordTableBorder
+ {
+ /// Top outer border of the table.
+ public MiniWordBorder TopBorder { get; set; }
+
+ /// Bottom outer border of the table.
+ public MiniWordBorder BottomBorder { get; set; }
+
+ /// Left outer border of the table.
+ public MiniWordBorder LeftBorder { get; set; }
+
+ /// Right outer border of the table.
+ public MiniWordBorder RightBorder { get; set; }
+
+ /// Horizontal border between rows (inside the table).
+ public MiniWordBorder InsideHorizontalBorder { get; set; }
+
+ /// Vertical border between columns (inside the table).
+ public MiniWordBorder InsideVerticalBorder { get; set; }
+
+ ///
+ /// Returns a where all six border edges use the
+ /// same style, color and size.
+ ///
+ /// Hex color string, e.g. "#000000" or "000000".
+ /// Border width in eighths of a point (default 4 = 0.5 pt).
+ /// Border line style (default ).
+ public static MiniWordTableBorder Create(string color = "000000", uint size = 4)
+ {
+ return Create(color, size, BorderValues.Single);
+ }
+
+ ///
+ /// Returns a where all six border edges use the
+ /// same style, color and size.
+ ///
+ /// Hex color string, e.g. "#000000" or "000000".
+ /// Border width in eighths of a point.
+ /// Border line style.
+ public static MiniWordTableBorder Create(string color, uint size, BorderValues style)
+ {
+ var normalizedColor = color?.Replace("#", "") ?? "000000";
+ return new MiniWordTableBorder
+ {
+ TopBorder = new MiniWordBorder { Color = normalizedColor, Size = size, Style = style },
+ BottomBorder = new MiniWordBorder { Color = normalizedColor, Size = size, Style = style },
+ LeftBorder = new MiniWordBorder { Color = normalizedColor, Size = size, Style = style },
+ RightBorder = new MiniWordBorder { Color = normalizedColor, Size = size, Style = style },
+ InsideHorizontalBorder = new MiniWordBorder { Color = normalizedColor, Size = size, Style = style },
+ InsideVerticalBorder = new MiniWordBorder { Color = normalizedColor, Size = size, Style = style }
+ };
+ }
+ }
+}
diff --git a/tests/MiniWordTests/IssueTests.cs b/tests/MiniWordTests/IssueTests.cs
index c4acfab..df17bf3 100644
--- a/tests/MiniWordTests/IssueTests.cs
+++ b/tests/MiniWordTests/IssueTests.cs
@@ -871,5 +871,61 @@ public class User
public string department { get; set; }
}
#endregion
+
+ [Fact]
+ public void TestTableBorder_AllBorders()
+ {
+ var path = PathHelper.GetTempFilePath();
+ var templatePath = PathHelper.GetTempFilePath();
+ TableBorderTestHelper.CreateTableBorderTemplate(templatePath);
+
+ var value = new Dictionary
+ {
+ ["TableBorder"] = MiniWordTableBorder.Create("#000000", 6),
+ ["Items"] = new List>
+ {
+ new Dictionary { { "Name", "Item1" }, { "Value", "10" } },
+ new Dictionary { { "Name", "Item2" }, { "Value", "20" } }
+ }
+ };
+
+ MiniWord.SaveAsByTemplate(path, templatePath, value);
+
+ var xml = Helpers.GetZipFileContent(path, "word/document.xml");
+ Assert.Contains("w:tblBorders", xml);
+ Assert.Contains("000000", xml);
+ Assert.Contains("Item1", xml);
+ Assert.Contains("Item2", xml);
+ }
+
+ [Fact]
+ public void TestTableBorder_CustomEdges()
+ {
+ var path = PathHelper.GetTempFilePath();
+ var templatePath = PathHelper.GetTempFilePath();
+ TableBorderTestHelper.CreateTableBorderTemplate(templatePath);
+
+ var value = new Dictionary
+ {
+ ["TableBorder"] = new MiniWordTableBorder
+ {
+ TopBorder = new MiniWordBorder { Style = BorderValues.Double, Color = "FF0000", Size = 8 },
+ BottomBorder = new MiniWordBorder { Style = BorderValues.Double, Color = "FF0000", Size = 8 },
+ LeftBorder = new MiniWordBorder { Style = BorderValues.Single, Color = "0000FF", Size = 4 },
+ RightBorder = new MiniWordBorder { Style = BorderValues.Single, Color = "0000FF", Size = 4 },
+ },
+ ["Items"] = new List>
+ {
+ new Dictionary { { "Name", "Alpha" }, { "Value", "1" } }
+ }
+ };
+
+ MiniWord.SaveAsByTemplate(path, templatePath, value);
+
+ var xml = Helpers.GetZipFileContent(path, "word/document.xml");
+ Assert.Contains("w:tblBorders", xml);
+ Assert.Contains("FF0000", xml);
+ Assert.Contains("0000FF", xml);
+ }
}
}
diff --git a/tests/MiniWordTests/MiniWordTests.csproj b/tests/MiniWordTests/MiniWordTests.csproj
index 6b0cb5b..92eff17 100644
--- a/tests/MiniWordTests/MiniWordTests.csproj
+++ b/tests/MiniWordTests/MiniWordTests.csproj
@@ -1,7 +1,7 @@
- net6.0;
+ net8.0;
false
diff --git a/tests/MiniWordTests/TableBorderTestHelper.cs b/tests/MiniWordTests/TableBorderTestHelper.cs
new file mode 100644
index 0000000..af98c1d
--- /dev/null
+++ b/tests/MiniWordTests/TableBorderTestHelper.cs
@@ -0,0 +1,40 @@
+using DocumentFormat.OpenXml;
+using DocumentFormat.OpenXml.Packaging;
+using DocumentFormat.OpenXml.Wordprocessing;
+
+namespace MiniWordTests
+{
+ internal static class TableBorderTestHelper
+ {
+ ///
+ /// Creates a minimal Word template that contains a table with:
+ ///
+ /// - A header row with static text.
+ /// - A data row with {{Items.Name}} and {{Items.Value}} placeholders.
+ /// - A dedicated border-control row with a {{TableBorder}} placeholder.
+ ///
+ ///
+ internal static void CreateTableBorderTemplate(string filePath)
+ {
+ using var doc = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document);
+ var mainPart = doc.AddMainDocumentPart();
+ mainPart.Document = new Document(
+ new Body(
+ new Table(
+ new TableRow(
+ new TableCell(new Paragraph(new Run(new Text("Name")))),
+ new TableCell(new Paragraph(new Run(new Text("Value")))),
+ new TableCell(new Paragraph(new Run(new Text("{{TableBorder}}"))))
+ ),
+ new TableRow(
+ new TableCell(new Paragraph(new Run(new Text("{{Items.Name}}")))),
+ new TableCell(new Paragraph(new Run(new Text("{{Items.Value}}")))),
+ new TableCell(new Paragraph(new Run(new Text(""))))
+ )
+ )
+ )
+ );
+ doc.Save();
+ }
+ }
+}