Repository: asciidoctor/asciidoctor-maven-plugin (java)

Issue:
Fix background color leaking into tables in asciidoctor-parser-doxia-module
**What is this issue about?**

- [x] Bug report 🪲
- [ ] Feature request 🏎
- [ ] Question ❓ Consider dropping it first in the [Project Chat](https://asciidoctor.zulipchat.com/#narrow/stream/users)


**Description**

The default style does not enforce the background color for table, and that causes the color of the enclosing element to leak.

For example, see this table in an example block.

![image](https://github.com/user-attachments/assets/0cf1467f-7127-48cd-aa10-b22b5a545111)

vs the original Asciidocotor conversion

![image](https://github.com/user-attachments/assets/dd4bcf0a-cbf0-43cb-9eda-207971d6436e)



Pull request description:
(main) Force white background in parser-doxia-module tables
**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Documentation
- [ ] Refactor
- [ ] Build improvement
- [ ] Other (please describe)


**What is the goal of this pull request?**

This prevents other colors from leaking into white rows. For example when the table is in an example block.

**Are there any alternative ways to implement this?**

Probably, I am by no means a CSS expert.

**Are there any implications of this pull request? Anything a user must know?**

No

**Is it related to an existing issue?**
<!-- If Yes, please add a line of the form: `Fixes #Issue` --> 
- [x] Yes
- [ ] No

Fixes #948


Patch:
diff --git a/asciidoctor-maven-commons/pom.xml b/asciidoctor-maven-commons/pom.xml
index 7b0a296..3c427e2 100644
--- a/asciidoctor-maven-commons/pom.xml
+++ b/asciidoctor-maven-commons/pom.xml
@@ -45,6 +45,12 @@
             <version>2.0.0</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.maven.doxia</groupId>
+            <artifactId>doxia-core</artifactId>
+            <version>2.0.0</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessor.java b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessor.java
index 4cdc6d1..83bed26 100644
--- a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessor.java
+++ b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessor.java
@@ -7,7 +7,7 @@ import org.asciidoctor.ast.StructuralNode;
 import org.asciidoctor.maven.site.parser.NodeProcessor;
 import org.asciidoctor.maven.site.parser.NodeSinker;
 
-import static javax.swing.text.html.HTML.Attribute.STYLE;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.STYLE;
 import static org.asciidoctor.maven.commons.StringUtils.isNotBlank;
 
 /**
@@ -50,20 +50,20 @@ public class ExampleNodeProcessor extends AbstractSinkNodeProcessor implements N
 
         final List<StructuralNode> blocks = node.getBlocks();
         if (!blocks.isEmpty()) {
-            divWrap(sink, node, () -> blocks.forEach(this::sink));
+            divWrap(sink, () -> blocks.forEach(this::sink));
         } else {
             // For :content_model: simple (inline)
             // https://docs.asciidoctor.org/asciidoc/latest/blocks/example-blocks/#example-style-syntax
             final String content = (String) node.getContent();
             if (isNotBlank(content)) {
-                divWrap(sink, node, () -> sink.rawText(content));
+                divWrap(sink, () -> sink.rawText(content));
             }
         }
 
         sink.division_();
     }
 
-    void divWrap(Sink sink, StructuralNode node, Runnable consumer) {
+    void divWrap(Sink sink, Runnable consumer) {
         sink.division(SinkAttributes.of(STYLE, Styles.EXAMPLE));
         consumer.run();
         sink.division_();
diff --git a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ImageNodeProcessor.java b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ImageNodeProcessor.java
index e6e77dd..ec127f7 100644
--- a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ImageNodeProcessor.java
+++ b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ImageNodeProcessor.java
@@ -7,8 +7,8 @@ import org.asciidoctor.ast.StructuralNode;
 import org.asciidoctor.maven.site.parser.NodeProcessor;
 import org.asciidoctor.maven.site.parser.NodeSinker;
 
-import static javax.swing.text.html.HTML.Attribute.ALT;
-import static javax.swing.text.html.HTML.Attribute.STYLE;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.ALT;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.STYLE;
 import static org.asciidoctor.maven.commons.StringUtils.isBlank;
 import static org.asciidoctor.maven.commons.StringUtils.isNotBlank;
 
diff --git a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/SinkAttributes.java b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/SinkAttributes.java
index 6636e74..d2ab3d6 100644
--- a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/SinkAttributes.java
+++ b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/SinkAttributes.java
@@ -1,16 +1,13 @@
 package org.asciidoctor.maven.site.parser.processors;
 
-import javax.swing.text.html.HTML.Attribute;
-
 import org.apache.maven.doxia.sink.SinkEventAttributes;
 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
 
 class SinkAttributes {
 
-    static SinkEventAttributes of(Attribute name, String value) {
+    static SinkEventAttributes of(String name, String value) {
         final var attributes = new SinkEventAttributeSet();
         attributes.addAttribute(name, value);
         return attributes;
     }
-
 }
diff --git a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/Styles.java b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/Styles.java
index 58f5667..3d5f012 100644
--- a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/Styles.java
+++ b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/Styles.java
@@ -19,4 +19,7 @@ class Styles {
         "margin-bottom: 1.25em",
         "padding: 1.25em"
     ).collect(Collectors.joining("; "));
+
+    public static final String TABLE = "background: #FFFFFF";
+
 }
diff --git a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessor.java b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessor.java
index 3076e20..598c7f4 100644
--- a/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessor.java
+++ b/asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessor.java
@@ -10,8 +10,8 @@ import org.asciidoctor.jruby.ast.impl.TableImpl;
 import org.asciidoctor.maven.site.parser.NodeProcessor;
 import org.asciidoctor.maven.site.parser.NodeSinker;
 
-import static javax.swing.text.html.HTML.Attribute.STYLE;
 import static org.apache.maven.doxia.sink.Sink.JUSTIFY_LEFT;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.STYLE;
 import static org.asciidoctor.maven.commons.StringUtils.isNotBlank;
 
 /**
@@ -42,7 +42,7 @@ public class TableNodeProcessor extends AbstractSinkNodeProcessor implements Nod
         final TableImpl tableNode = (TableImpl) node;
 
         final Sink sink = getSink();
-        sink.table();
+        sink.table(SinkAttributes.of(STYLE, Styles.TABLE));
         sink.tableRows(new int[]{JUSTIFY_LEFT}, false);
         final List<Row> header = tableNode.getHeader();
         final List<Row> rows = tableNode.getBody();
@@ -92,7 +92,6 @@ public class TableNodeProcessor extends AbstractSinkNodeProcessor implements Nod
         final String title = TitleCaptionExtractor.getText(node);
         if (isNotBlank(title)) {
             // Contrary to other cases where we use <div>, we use <caption>: same as Fluido and Asciidoctor
-            // TODO Have a proper CSS stylesheet injected
             sink.tableCaption(SinkAttributes.of(STYLE, Styles.CAPTION + "; text-align: left"));
             // getCaption returns
             // - "" when '[caption=]'


Test patch:
diff --git a/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessorTest.java b/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessorTest.java
index 2e7abfb..94a3ae8 100644
--- a/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessorTest.java
+++ b/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/ExampleNodeProcessorTest.java
@@ -3,6 +3,7 @@ package org.asciidoctor.maven.site.parser.processors;
 import java.io.StringWriter;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import org.asciidoctor.Asciidoctor;
@@ -20,9 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 @NodeProcessorTest(ExampleNodeProcessor.class)
 class ExampleNodeProcessorTest {
 
-    public static final String EXAMPLE_TITLE_OPENING = "<div style=\"color: #7a2518; margin-bottom: .25em\">";
-    public static final String EXAMPLE_CONTENT_OPENING = "<div style=\"background: #fffef7; border-color: #e0e0dc; border: 1px solid #e6e6e6; box-shadow: 0 1px 4px #e0e0dc; margin-bottom: 1.25em; padding: 1.25em\">";
-
     private Asciidoctor asciidoctor;
     private NodeProcessor nodeProcessor;
     private StringWriter sinkWriter;
@@ -45,9 +43,7 @@ class ExampleNodeProcessorTest {
 
         assertThat(html)
             .isEqualTo(div(
-                EXAMPLE_CONTENT_OPENING +
-                    p("SomeText") +
-                    "</div>"));
+                exampleContentDiv(p("SomeText"))));
     }
 
     @Test
@@ -57,11 +53,7 @@ class ExampleNodeProcessorTest {
         String html = process(content);
 
         assertThat(html)
-            .isEqualTo(div(
-                EXAMPLE_TITLE_OPENING + "Example 1. The title</div>" +
-                    EXAMPLE_CONTENT_OPENING +
-                    p("SomeText") +
-                    "</div>"));
+            .isEqualTo(div(exampleTitleDiv("Example 1. The title") + exampleContentDiv(p("SomeText"))));
     }
 
     @Test
@@ -71,11 +63,7 @@ class ExampleNodeProcessorTest {
         String html = process(content);
 
         assertThat(html)
-            .isEqualTo(div(
-                EXAMPLE_TITLE_OPENING + "The title</div>" +
-                    EXAMPLE_CONTENT_OPENING +
-                    p("SomeText") +
-                    "</div>"));
+            .isEqualTo(div(exampleTitleDiv("The title") + exampleContentDiv(p("SomeText"))));
     }
 
     @Test
@@ -87,9 +75,9 @@ class ExampleNodeProcessorTest {
 
         assertThat(html)
             .isEqualTo(div(
-                EXAMPLE_CONTENT_OPENING +
-                    p("<a href=\"https://docs.asciidoctor.org/\" class=\"bare\">https://docs.asciidoctor.org/</a>") +
-                    "</div>"));
+                exampleContentDiv(
+                    p("<a href=\"https://docs.asciidoctor.org/\" class=\"bare\">https://docs.asciidoctor.org/</a>")
+                )));
     }
 
     @Test
@@ -105,12 +93,12 @@ class ExampleNodeProcessorTest {
 
         assertThat(html)
             .isEqualTo(div(
-                EXAMPLE_CONTENT_OPENING +
-                    "<table class=\"bodyTable\">" +
-                    "<tr class=\"a\"><td style=\"text-align: left;\">Header 1</td><td>Header 2</td></tr>" +
-                    "<tr class=\"b\"><td style=\"text-align: left;\">Column 1, row 1</td><td>Column 2, row 1</td></tr>" +
-                    "</table>" +
-                    "</div>"));
+                exampleContentDiv(
+                    "<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
+                        tr("a", td("Header 1", Map.of("style", "text-align: left;")) + td("Header 2")) +
+                        tr("b", td("Column 1, row 1", Map.of("style", "text-align: left;")) + td("Column 2, row 1")) +
+                        "</table>"
+                )));
     }
 
     @Test
@@ -125,12 +113,10 @@ class ExampleNodeProcessorTest {
 
         assertThat(html)
             .isEqualTo(div(
-                EXAMPLE_CONTENT_OPENING +
-                    ul(
-                        li("Un" + ul(li("Dos"))) +
-                            li("Tres" + ul(li("Quatre")))
-                    ) +
-                    "</div>"));
+                exampleContentDiv(ul(
+                    li("Un" + ul(li("Dos"))),
+                    li("Tres" + ul(li("Quatre")))
+                ))));
     }
 
     @Test
@@ -146,13 +132,13 @@ class ExampleNodeProcessorTest {
 
         assertThat(html)
             .isEqualTo(div(
-                EXAMPLE_CONTENT_OPENING +
+                exampleContentDiv(
                     ul(
-                        li("Un" + ul(li("Dos"))) +
-                            li("Tres" + ul(li("Quatre")))
+                        li("Un" + ul(li("Dos"))),
+                        li("Tres" + ul(li("Quatre")))
                     ) +
-                    p("<a href=\"https://docs.asciidoctor.org/\" class=\"bare\">https://docs.asciidoctor.org/</a>") +
-                    "</div>"));
+                        p("<a href=\"https://docs.asciidoctor.org/\" class=\"bare\">https://docs.asciidoctor.org/</a>")
+                )));
     }
 
     private String documentWithExample(String text, String title, List<String> attributes) {
@@ -187,10 +173,7 @@ class ExampleNodeProcessorTest {
 
             // Content is directly embedded instead of delegated to paragraph processor
             assertThat(html)
-                .isEqualTo(div(
-                    EXAMPLE_CONTENT_OPENING +
-                        "SomeText" +
-                        "</div>"));
+                .isEqualTo(div(exampleContentDiv("SomeText")));
         }
 
         @Test
@@ -203,11 +186,7 @@ class ExampleNodeProcessorTest {
             String html = process(content);
 
             assertThat(html)
-                .isEqualTo(div(
-                    EXAMPLE_TITLE_OPENING + "Example 1. Optional title</div>" +
-                        EXAMPLE_CONTENT_OPENING +
-                        "SomeText" +
-                        "</div>"));
+                .isEqualTo(div(exampleTitleDiv("Example 1. Optional title") + exampleContentDiv("SomeText")));
         }
 
         @Test
@@ -220,10 +199,15 @@ class ExampleNodeProcessorTest {
             String html = process(content);
 
             assertThat(html)
-                .isEqualTo(div(
-                        EXAMPLE_CONTENT_OPENING +
-                        "SomeText, <a href=\"https://docs.asciidoctor.org/\" class=\"bare\">https://docs.asciidoctor.org/</a>" +
-                        "</div>"));
+                .isEqualTo(div(exampleContentDiv("SomeText, <a href=\"https://docs.asciidoctor.org/\" class=\"bare\">https://docs.asciidoctor.org/</a>")));
         }
     }
+
+    private static String exampleTitleDiv(String text) {
+        return div("color: #7a2518; margin-bottom: .25em", text);
+    }
+
+    private static String exampleContentDiv(String text) {
+        return div("background: #fffef7; border-color: #e0e0dc; border: 1px solid #e6e6e6; box-shadow: 0 1px 4px #e0e0dc; margin-bottom: 1.25em; padding: 1.25em", text);
+    }
 }
diff --git a/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessorTest.java b/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessorTest.java
index 3e7b866..f623fa3 100644
--- a/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessorTest.java
+++ b/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/TableNodeProcessorTest.java
@@ -64,13 +64,13 @@ class TableNodeProcessorTest {
 
         // Header for now is just first row with class=a
         assertThat(html)
-            .isEqualTo("<table class=\"bodyTable\">" +
+            .isEqualTo("<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
                 "<tr class=\"a\">" +
                 "<th>Name</th>" +
                 "<th>Language</th></tr>" +
                 "<tr class=\"b\">" +
-                "<td style=\"text-align: left;\">JRuby</td>" +
-                "<td>Java</td></tr>" +
+                td("JRuby", Map.of("style", "text-align: left;")) +
+                td("Java") + "</tr>" +
                 "<tr class=\"a\">" +
                 "<td style=\"text-align: left;\">Rubinius</td>" +
                 "<td>Ruby</td></tr></table>");
@@ -83,7 +83,7 @@ class TableNodeProcessorTest {
         String html = process(content);
 
         assertThat(html)
-            .isEqualTo(removeLineBreaks("<table class=\"bodyTable\">" +
+            .isEqualTo(removeLineBreaks("<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
                 "<tr class=\"a\">" +
                 "<td style=\"text-align: left;\">JRuby</td>" +
                 "<td>Java</td></tr>" +
@@ -109,7 +109,8 @@ class TableNodeProcessorTest {
         String html = process(content);
 
         assertThat(html)
-            .isEqualTo("<table class=\"bodyTable\"><caption style=\"" + CAPTION_STYLE + "\">Table 1. Table caption&#8230;&#8203;or title</caption>" +
+            .isEqualTo("<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
+                "<caption style=\"" + CAPTION_STYLE + "\">Table 1. Table caption&#8230;&#8203;or title</caption>" +
                 "<tr class=\"a\">" +
                 "<td style=\"text-align: left;\">JRuby</td>" +
                 "<td>Java</td></tr>" +
@@ -151,7 +152,7 @@ class TableNodeProcessorTest {
             String html = process(content);
 
             assertThat(html)
-                .isEqualTo("<table class=\"bodyTable\">" +
+                .isEqualTo("<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
                     tr("a", td("JRuby", textAlignLeft()) + td("Java")) +
                     tr("b", td("Rubinius", textAlignLeft()) + td("Ruby")) +
                     tr("a",
@@ -168,7 +169,7 @@ class TableNodeProcessorTest {
             String html = process(content);
 
             assertThat(html)
-                .isEqualTo("<table class=\"bodyTable\">" +
+                .isEqualTo("<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
                     tr("a", td("JRuby", textAlignLeft()) + td("Java")) +
                     tr("b", td("Rubinius", textAlignLeft()) + td("Ruby")) +
                     tr("a",
@@ -185,7 +186,7 @@ class TableNodeProcessorTest {
             String html = process(content);
 
             assertThat(html)
-                .isEqualTo("<table class=\"bodyTable\">" +
+                .isEqualTo("<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
                     tr("a", td("JRuby", textAlignLeft()) + td("Java")) +
                     tr("b", td("Rubinius", textAlignLeft()) + td("Ruby")) +
                     tr("a",
@@ -200,12 +201,12 @@ class TableNodeProcessorTest {
     }
 
     private static String expectedNoLabelBeginning() {
-        return "<table class=\"bodyTable\">" +
+        return "<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
             "<caption style=\"" + CAPTION_STYLE + "\">Table caption&#8230;&#8203;or title</caption>";
     }
 
     private static String expectedTableWithoutLabel() {
-        return "<table class=\"bodyTable\">" +
+        return "<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
             "<caption style=\"" + CAPTION_STYLE + "\">Table caption&#8230;&#8203;or title</caption>" +
             "<tr class=\"a\">" +
             "<td style=\"text-align: left;\">JRuby</td>" +
@@ -216,7 +217,7 @@ class TableNodeProcessorTest {
     }
 
     private static String expectedTableWithoutCaption() {
-        return "<table class=\"bodyTable\">" +
+        return "<table class=\"bodyTable\" style=\"background: #FFFFFF\">" +
             "<tr class=\"a\">" +
             "<td style=\"text-align: left;\">JRuby</td>" +
             "<td>Java</td></tr>" +
diff --git a/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/test/Html.java b/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/test/Html.java
index b01147c..050a56f 100644
--- a/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/test/Html.java
+++ b/asciidoctor-parser-doxia-module/src/test/java/org/asciidoctor/maven/site/parser/processors/test/Html.java
@@ -22,8 +22,12 @@ public class Html {
         return htmlElement("code", text);
     }
 
-    public static String div(String text) {
-        return htmlElement("div", text);
+    public static String div(String... elements) {
+        return htmlElement("div", String.join("", elements));
+    }
+
+    public static String div(String style, String text) {
+        return htmlElement("div", text, Map.of(STYLE, style));
     }
 
     public static String ul(String... elements) {