diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2f54dc0d3..3201cb851 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -114,6 +114,12 @@ jobs:
- name: Install root artifact
run: ./mvnw -B -ntp -DskipTests install -pl .
+ - name: Install graph-compose-render-docx (consumed by the examples module)
+ # The DOCX export example depends on the render-docx backend module, which
+ # is not on Central. Install it after the engine (its graph-compose
+ # dependency) so the examples build resolves it.
+ run: ./mvnw -B -ntp -f render-docx/pom.xml -DskipTests install
+
- name: Compile examples module
run: ./mvnw -B -ntp -f examples/pom.xml clean compile
@@ -137,34 +143,6 @@ jobs:
path: examples/target/generated-pdfs/**
if-no-files-found: error
- no-poi-suite:
- name: Test suite without poi-ooxml (optional-deps regression)
- if: github.event_name == 'pull_request'
- needs: architecture-and-documentation-guards
- runs-on: ubuntu-latest
- env:
- JAVA_TOOL_OPTIONS: -Djava.awt.headless=true
-
- steps:
- - name: Check out repository
- uses: actions/checkout@v7
-
- - name: Set up Temurin JDK 17
- uses: actions/setup-java@v5
- with:
- distribution: temurin
- java-version: '17'
- cache: maven
-
- - name: Run suite under -P no-poi
- # poi-ooxml is declared true; this job
- # validates that callers who don't render DOCX still get a
- # green suite without the POI footprint. Tests that require
- # poi (DocxSemanticBackend output) carry
- # @DisabledIfSystemProperty(named = "no.poi", matches = "true")
- # and skip cleanly. See Track I1 in the readiness taskboard.
- run: ./mvnw -B -ntp test -pl . -P no-poi
-
binary-compat:
name: Binary Compatibility (japicmp vs pom baseline)
if: github.event_name == 'pull_request'
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 7e6e26c35..f0be42a20 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -99,6 +99,16 @@ jobs:
CENTRAL_TOKEN: ${{ secrets.CENTRAL_TOKEN }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+ - name: Publish render-docx to Maven Central
+ # The semantic DOCX/PPTX backend, lockstep-versioned with the engine
+ # (ships on the same v* tag). graph-compose resolves from the local repo
+ # installed by the engine deploy above.
+ run: ./mvnw -B -ntp -f render-docx/pom.xml -P release -DskipTests -Dgpg.skip=false deploy
+ env:
+ CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
+ CENTRAL_TOKEN: ${{ secrets.CENTRAL_TOKEN }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+
- name: Publish bundle to Maven Central
# The graph-compose-bundle convenience aggregate pins this engine
# version + a compatible graph-compose-fonts version. It tracks the
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d79aed96b..e94fac197 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,12 @@ for this cycle.
(core + render-pdf + templates + fonts + emoji). The optional
`graph-compose-fonts` and `graph-compose-emoji` artifacts are unchanged. Full
install guidance ships with 2.0.0.
+- `graph-compose-render-docx` is now a separate artifact: the semantic DOCX/PPTX
+ backend and Apache POI leave the `graph-compose` jar. `DocxSemanticBackend` moves
+ to `com.demcha.compose.document.backend.semantic.docx` and `PptxSemanticBackend`
+ to `com.demcha.compose.document.backend.semantic.pptx`; add
+ `graph-compose-render-docx` (it brings POI transitively) to export DOCX. The
+ `no-poi` build profile is retired.
## v1.9.0 — 2026-06-29
diff --git a/aggregator/pom.xml b/aggregator/pom.xml
index a8e19102a..2f5361242 100644
--- a/aggregator/pom.xml
+++ b/aggregator/pom.xml
@@ -60,6 +60,7 @@
..
../fonts
../emoji
+ ../render-docx
../bundle
../examples
../benchmarks
diff --git a/docs/recipes/docx-export.md b/docs/recipes/docx-export.md
index 701bfd2af..8bdb898cf 100644
--- a/docs/recipes/docx-export.md
+++ b/docs/recipes/docx-export.md
@@ -9,7 +9,7 @@ the recipient needs to *edit* the document; use PDF when pixels must match.
## Exporting a session
```java
-import com.demcha.compose.document.backend.semantic.DocxSemanticBackend;
+import com.demcha.compose.document.backend.semantic.docx.DocxSemanticBackend;
try (DocumentSession document = GraphCompose.document()
.pageSize(595, 842)
@@ -32,10 +32,10 @@ try (DocumentSession document = GraphCompose.document()
default output file when one was given to `GraphCompose.document(path)`;
the two-argument overload targets an explicit path.
-**Dependency note:** the backend requires `org.apache.poi:poi-ooxml` on
-the classpath. GraphCompose declares it **optional** in its POM, so
-consumers who export DOCX must add it explicitly — consumers who only
-render PDF carry no POI footprint.
+**Dependency note:** the DOCX backend ships in the
+`io.github.demchaav:graph-compose-render-docx` artifact, which brings Apache POI
+transitively. Add that one dependency to export DOCX — consumers who only render
+PDF never pull POI.
## What maps 1:1
@@ -77,4 +77,4 @@ designs, precise placement — export PDF for the reader and DOCX only as
an editable companion.
Round-trip coverage (paragraphs, tables, metadata, chart fallback) lives in
-[`DocxSemanticBackendTest`](../../src/test/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackendTest.java).
+[`DocxSemanticBackendTest`](../../render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackendTest.java).
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 067ef763f..3537240bb 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -43,14 +43,14 @@ GraphCompose marks one heavy, rarely-needed dependency **optional** so
PDF-only consumers don't pay for it. If you use DOCX export, add the
dependency to **your** project.
-**DOCX export** — `document.export(new DocxSemanticBackend())` needs
-Apache POI on your classpath:
+**DOCX export** — `document.export(new DocxSemanticBackend())` ships in the
+`graph-compose-render-docx` artifact, which brings Apache POI transitively:
```xml
- org.apache.poi
- poi-ooxml
- 5.5.1
+ io.github.demchaav
+ graph-compose-render-docx
+ 2.0.0
```
diff --git a/examples/README.md b/examples/README.md
index 29f8ccc39..4d222bb27 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -943,8 +943,8 @@ images, spacers, and page breaks map 1:1; session metadata lands in the
Word core properties. Charts export as their categories-by-series data
table (one capability warning per export), shape containers flatten to
inline layers, and pure geometry — dividers, shapes, barcodes — stays
-PDF-only by design. Requires `org.apache.poi:poi-ooxml` on the
-classpath (the dependency is optional in the GraphCompose POM).
+PDF-only by design. Requires the `graph-compose-render-docx` artifact
+on the classpath (it brings Apache POI transitively).
[📄 View PDF](../assets/readme/examples/word-export-companion.pdf) ·
[📝 Word file](../assets/readme/examples/word-export-companion.docx) ·
diff --git a/examples/pom.xml b/examples/pom.xml
index 2c65ac5b1..c96aad7fe 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -67,13 +67,13 @@
${graphcompose.emoji.version}
-
+
- org.apache.poi
- poi-ooxml
- 5.5.1
+ io.github.demchaav
+ graph-compose-render-docx
+ ${graphcompose.version}
diff --git a/examples/src/main/java/com/demcha/examples/features/docx/WordExportExample.java b/examples/src/main/java/com/demcha/examples/features/docx/WordExportExample.java
index e16da1429..86e9ff5cb 100644
--- a/examples/src/main/java/com/demcha/examples/features/docx/WordExportExample.java
+++ b/examples/src/main/java/com/demcha/examples/features/docx/WordExportExample.java
@@ -2,7 +2,7 @@
import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentSession;
-import com.demcha.compose.document.backend.semantic.DocxSemanticBackend;
+import com.demcha.compose.document.backend.semantic.docx.DocxSemanticBackend;
import com.demcha.compose.document.chart.ChartData;
import com.demcha.compose.document.chart.ChartSpec;
import com.demcha.compose.document.image.DocumentImageData;
diff --git a/pom.xml b/pom.xml
index b6b2b5474..9354ac2c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,6 @@
1.5.37
1.18.46
3.0.7
- 5.5.1
21.0.2
2.0.18
3.5.4
@@ -182,19 +181,6 @@
${flexmark.version}
-
-
- org.apache.poi
- poi-ooxml
- ${poi.version}
- true
-
-
com.fasterxml.jackson.core
jackson-annotations
@@ -649,39 +635,6 @@
-
-
- no-poi
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${maven.surefire.plugin.version}
-
-
- org.apache.poi:poi-ooxml
- org.apache.poi:poi
- org.apache.poi:poi-ooxml-lite
-
-
- true
-
-
-
-
-
-
-
+ io.github.demchaav
+ graph-compose-render-docx
+ 2.0.0-SNAPSHOT
+
+ GraphCompose Render — DOCX
+ Semantic DOCX / PPTX export backend for GraphCompose, backed by Apache POI.
+ https://github.com/DemchaAV/GraphCompose
+
+
+
+ MIT License
+ https://opensource.org/licenses/MIT
+ repo
+
+
+
+
+
+ DemchaAV
+ Artem Demchyshyn
+ demchishynartem@gmail.com
+ https://github.com/DemchaAV
+
+ Lead Developer
+ Architect
+
+ UTC 0
+
+
+
+
+ scm:git:https://github.com/DemchaAV/GraphCompose.git
+ scm:git:ssh://git@github.com/DemchaAV/GraphCompose.git
+ https://github.com/DemchaAV/GraphCompose/tree/main
+
+
+
+ UTF-8
+ 17
+
+ 5.5.1
+ 6.1.1
+ 3.27.7
+ 1.5.37
+ 1.0.0
+ 1.0.0
+
+ 3.15.0
+ 3.5.0
+ 3.5.6
+ 3.4.0
+ 3.12.0
+ 3.2.8
+ 0.11.0
+
+
+ true
+
+
+
+
+ io.github.demchaav
+ graph-compose
+ ${project.version}
+
+
+ org.apache.poi
+ poi-ooxml
+ ${poi.version}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.bom.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ test
+
+
+
+ io.github.demchaav
+ graph-compose-fonts
+ ${graphcompose.fonts.version}
+ test
+
+
+ io.github.demchaav
+ graph-compose-emoji
+ ${graphcompose.emoji.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.plugin.version}
+
+ ${maven.compiler.release}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surefire.plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven.jar.plugin.version}
+
+
+
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven.source.plugin.version}
+
+
+ attach-sources
+ package
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven.javadoc.plugin.version}
+
+
+ attach-javadocs
+ package
+
+ jar
+
+
+ none
+ false
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${maven.gpg.plugin.version}
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+ ${gpg.skip}
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central.publishing.plugin.version}
+ true
+
+ central
+ false
+ validated
+
+
+
+
+
+
+
diff --git a/src/main/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackend.java b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java
similarity index 97%
rename from src/main/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackend.java
rename to render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java
index 94ff939f1..a38c12b93 100644
--- a/src/main/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackend.java
+++ b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java
@@ -1,5 +1,7 @@
-package com.demcha.compose.document.backend.semantic;
+package com.demcha.compose.document.backend.semantic.docx;
+import com.demcha.compose.document.backend.semantic.SemanticBackend;
+import com.demcha.compose.document.backend.semantic.SemanticExportContext;
import com.demcha.compose.document.chart.ChartData;
import com.demcha.compose.document.chart.NumberFormatSpec;
import com.demcha.compose.document.dsl.TableBuilder;
@@ -56,9 +58,10 @@
* containers. It deliberately ignores fixed-layout concerns (no per-page
* pagination, no PDF chrome) since semantic exports target editing tools.
*
- * Dependencies: requires {@code org.apache.poi:poi-ooxml} on the
- * classpath. Library consumers must add it explicitly because the dependency is
- * declared optional in the GraphCompose POM.
+ * Dependencies: this backend ships in
+ * {@code io.github.demchaav:graph-compose-render-docx}, which brings
+ * {@code org.apache.poi:poi-ooxml} transitively — adding that one artifact is
+ * all a DOCX consumer needs.
*
* @author Artem Demchyshyn
*/
diff --git a/src/main/java/com/demcha/compose/document/backend/semantic/PptxSemanticBackend.java b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/pptx/PptxSemanticBackend.java
similarity index 87%
rename from src/main/java/com/demcha/compose/document/backend/semantic/PptxSemanticBackend.java
rename to render-docx/src/main/java/com/demcha/compose/document/backend/semantic/pptx/PptxSemanticBackend.java
index da8d5b90b..07a357382 100644
--- a/src/main/java/com/demcha/compose/document/backend/semantic/PptxSemanticBackend.java
+++ b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/pptx/PptxSemanticBackend.java
@@ -1,5 +1,8 @@
-package com.demcha.compose.document.backend.semantic;
+package com.demcha.compose.document.backend.semantic.pptx;
+import com.demcha.compose.document.backend.semantic.SemanticBackend;
+import com.demcha.compose.document.backend.semantic.SemanticExportContext;
+import com.demcha.compose.document.backend.semantic.SemanticExportManifest;
import com.demcha.compose.document.layout.DocumentGraph;
import com.demcha.compose.document.exceptions.UnsupportedNodeCapabilityException;
import com.demcha.compose.document.node.ContainerNode;
diff --git a/src/test/java/com/demcha/compose/document/backend/semantic/DocxGeometryDropTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxGeometryDropTest.java
similarity index 92%
rename from src/test/java/com/demcha/compose/document/backend/semantic/DocxGeometryDropTest.java
rename to render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxGeometryDropTest.java
index 0bb8a906c..03a0cf351 100644
--- a/src/test/java/com/demcha/compose/document/backend/semantic/DocxGeometryDropTest.java
+++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxGeometryDropTest.java
@@ -1,4 +1,4 @@
-package com.demcha.compose.document.backend.semantic;
+package com.demcha.compose.document.backend.semantic.docx;
import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentSession;
@@ -8,7 +8,6 @@
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import java.io.ByteArrayInputStream;
import java.util.List;
@@ -22,8 +21,6 @@
* geometry leaves (paths, polygons) are dropped without throwing — the
* fixed-layout PDF backend is the path for pixel-perfect geometry.
*/
-@DisabledIfSystemProperty(named = "no.poi", matches = "true",
- disabledReason = "DocxSemanticBackend requires poi-ooxml")
class DocxGeometryDropTest {
@Test
diff --git a/src/test/java/com/demcha/compose/document/backend/semantic/DocxListParityTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxListParityTest.java
similarity index 92%
rename from src/test/java/com/demcha/compose/document/backend/semantic/DocxListParityTest.java
rename to render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxListParityTest.java
index d417955c6..b5010aaad 100644
--- a/src/test/java/com/demcha/compose/document/backend/semantic/DocxListParityTest.java
+++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxListParityTest.java
@@ -1,4 +1,4 @@
-package com.demcha.compose.document.backend.semantic;
+package com.demcha.compose.document.backend.semantic.docx;
import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentSession;
@@ -7,7 +7,6 @@
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import java.io.ByteArrayInputStream;
import java.util.List;
@@ -22,8 +21,6 @@
* {@link ListMarker#normalizeItemText(String, boolean)} for flat items —
* so both outputs of one session agree.
*/
-@DisabledIfSystemProperty(named = "no.poi", matches = "true",
- disabledReason = "DocxSemanticBackend requires poi-ooxml; the no-poi profile validates the rest of the suite without it")
class DocxListParityTest {
@Test
diff --git a/src/test/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackendTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackendTest.java
similarity index 95%
rename from src/test/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackendTest.java
rename to render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackendTest.java
index 7491effc6..588cfed06 100644
--- a/src/test/java/com/demcha/compose/document/backend/semantic/DocxSemanticBackendTest.java
+++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackendTest.java
@@ -1,4 +1,4 @@
-package com.demcha.compose.document.backend.semantic;
+package com.demcha.compose.document.backend.semantic.docx;
import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentSession;
@@ -15,7 +15,6 @@
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import java.io.ByteArrayInputStream;
import java.util.List;
@@ -23,14 +22,8 @@
import static org.assertj.core.api.Assertions.assertThat;
/**
- * DOCX semantic backend output. Skipped under the {@code -P no-poi}
- * profile, where {@code poi-ooxml} is intentionally absent from the
- * test classpath to validate that consumers who don't render DOCX can
- * still build and run the canonical suite without the optional POI
- * footprint (Track I1).
+ * DOCX semantic backend output.
*/
-@DisabledIfSystemProperty(named = "no.poi", matches = "true",
- disabledReason = "DocxSemanticBackend requires poi-ooxml; the no-poi profile validates the rest of the suite without it")
class DocxSemanticBackendTest {
@Test
diff --git a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/SessionSemanticExportTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/SessionSemanticExportTest.java
new file mode 100644
index 000000000..0838cd706
--- /dev/null
+++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/SessionSemanticExportTest.java
@@ -0,0 +1,70 @@
+package com.demcha.compose.document.backend.semantic.docx;
+
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.backend.semantic.SemanticExportManifest;
+import com.demcha.compose.document.backend.semantic.pptx.PptxSemanticBackend;
+import com.demcha.compose.document.node.ContainerNode;
+import com.demcha.compose.document.node.ParagraphNode;
+import com.demcha.compose.document.node.ShapeNode;
+import com.demcha.compose.document.node.TextAlign;
+import com.demcha.compose.document.style.DocumentColor;
+import com.demcha.compose.document.style.DocumentInsets;
+import com.demcha.compose.document.style.DocumentStroke;
+import com.demcha.compose.document.style.DocumentTextStyle;
+import org.junit.jupiter.api.Test;
+
+import java.awt.Color;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Exercises {@code DocumentSession.export(...)} with the semantic office backends
+ * from their new home. Moved out of the engine's {@code DocumentSessionTest} when
+ * the DOCX/PPTX backends were extracted to graph-compose-render-docx (the engine
+ * test scope can no longer see them). POI is always present in this module, so the
+ * old no-poi guard is dropped.
+ */
+class SessionSemanticExportTest {
+
+ @Test
+ void semanticBackendsShouldExportManifestsFromDocumentGraph() throws Exception {
+ try (DocumentSession session = GraphCompose.document()
+ .pageSize(200, 200)
+ .margin(DocumentInsets.of(10))
+ .create()) {
+
+ session.add(new ContainerNode(
+ "DocxRoot",
+ List.of(new ParagraphNode("P", "Hello world", DocumentTextStyle.DEFAULT, TextAlign.LEFT, 0, DocumentInsets.zero(), DocumentInsets.zero())),
+ 8,
+ DocumentInsets.zero(),
+ DocumentInsets.zero(),
+ null,
+ null));
+
+ byte[] docx = session.export(new DocxSemanticBackend());
+
+ assertThat(docx).isNotEmpty();
+ // DOCX files are ZIP-archived OOXML packages; the first two bytes
+ // of any ZIP container are the local-file-header signature.
+ assertThat(docx[0]).isEqualTo((byte) 'P');
+ assertThat(docx[1]).isEqualTo((byte) 'K');
+
+ session.clear();
+ session.add(new ContainerNode(
+ "PptxRoot",
+ List.of(new ShapeNode("Box", 40, 20, Color.BLUE, DocumentStroke.of(DocumentColor.BLACK, 1), DocumentInsets.zero(), DocumentInsets.zero())),
+ 8,
+ DocumentInsets.zero(),
+ DocumentInsets.zero(),
+ null,
+ null));
+
+ SemanticExportManifest pptx = session.export(new PptxSemanticBackend());
+ assertThat(pptx.backendName()).isEqualTo("pptx-semantic");
+ assertThat(pptx.nodeKinds()).contains("ContainerNode", "ShapeNode");
+ }
+ }
+}
diff --git a/scripts/cut-release.ps1 b/scripts/cut-release.ps1
index 0e4d7f502..15c2360a9 100644
--- a/scripts/cut-release.ps1
+++ b/scripts/cut-release.ps1
@@ -479,6 +479,9 @@ try {
Update-PomVersion (Join-Path $repoRoot 'aggregator/pom.xml') $Version
Update-PomVersion (Join-Path $repoRoot 'examples/pom.xml') $Version
Update-PomVersion (Join-Path $repoRoot 'benchmarks/pom.xml') $Version
+ # render-docx tracks the engine line (lockstep): its bumps here and
+ # its graph-compose dep is ${project.version} (follows automatically).
+ Update-PomVersion (Join-Path $repoRoot 'render-docx/pom.xml') $Version
# Bundle tracks the engine line: its project bumps here; its
# graph-compose dep is ${project.version} (follows automatically) and its
# graph-compose-fonts dep is ${graphcompose.fonts.version} (stays pinned —
@@ -547,6 +550,7 @@ try {
'pom.xml',
'aggregator/pom.xml',
'bundle/pom.xml',
+ 'render-docx/pom.xml',
'examples/pom.xml',
'benchmarks/pom.xml',
'README.md',
diff --git a/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java b/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java
index f273924a7..4a5231147 100644
--- a/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java
+++ b/src/test/java/com/demcha/compose/document/api/DocumentSessionTest.java
@@ -19,10 +19,7 @@
import com.demcha.compose.engine.measurement.FontLibraryTextMeasurementSystem;
import com.demcha.compose.document.backend.fixed.FixedLayoutBackend;
import com.demcha.compose.document.backend.fixed.FixedLayoutRenderContext;
-import com.demcha.compose.document.backend.semantic.DocxSemanticBackend;
import com.demcha.compose.document.backend.fixed.pdf.PdfFixedLayoutBackend;
-import com.demcha.compose.document.backend.semantic.PptxSemanticBackend;
-import com.demcha.compose.document.backend.semantic.SemanticExportManifest;
import com.demcha.compose.document.exceptions.AtomicNodeTooLargeException;
import com.demcha.compose.document.image.DocumentImageFitMode;
import com.demcha.compose.document.layout.BoxConstraints;
@@ -74,7 +71,6 @@
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
@@ -787,48 +783,6 @@ void registryRegisterOnClosedSessionThrowsIllegalStateException() {
.isInstanceOf(IllegalStateException.class);
}
- @Test
- @DisabledIfSystemProperty(named = "no.poi", matches = "true",
- disabledReason = "Exercises DocxSemanticBackend; skipped under the no-poi profile that excludes poi-ooxml from the test classpath")
- void semanticBackendsShouldExportManifestsFromDocumentGraph() throws Exception {
- try (DocumentSession session = GraphCompose.document()
- .pageSize(200, 200)
- .margin(DocumentInsets.of(10))
- .create()) {
-
- session.add(new ContainerNode(
- "DocxRoot",
- List.of(new ParagraphNode("P", "Hello world", DocumentTextStyle.DEFAULT, TextAlign.LEFT, 0, DocumentInsets.zero(), DocumentInsets.zero())),
- 8,
- DocumentInsets.zero(),
- DocumentInsets.zero(),
- null,
- null));
-
- byte[] docx = session.export(new DocxSemanticBackend());
-
- assertThat(docx).isNotEmpty();
- // DOCX files are ZIP-archived OOXML packages; the first two bytes
- // of any ZIP container are the local-file-header signature.
- assertThat(docx[0]).isEqualTo((byte) 'P');
- assertThat(docx[1]).isEqualTo((byte) 'K');
-
- session.clear();
- session.add(new ContainerNode(
- "PptxRoot",
- List.of(new ShapeNode("Box", 40, 20, Color.BLUE, DocumentStroke.of(DocumentColor.BLACK, 1), DocumentInsets.zero(), DocumentInsets.zero())),
- 8,
- DocumentInsets.zero(),
- DocumentInsets.zero(),
- null,
- null));
-
- SemanticExportManifest pptx = session.export(new PptxSemanticBackend());
- assertThat(pptx.backendName()).isEqualTo("pptx-semantic");
- assertThat(pptx.nodeKinds()).contains("ContainerNode", "ShapeNode");
- }
- }
-
@Test
void documentLevelPdfOptionsShouldReuseCompiledLayout() throws Exception {
try (DocumentSession session = GraphCompose.document()