Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ For a Spring Boot `@RestController` streaming the PDF straight to the response,
| Format | Status | Notes |
|---|---|---|
| PDF | Production | Fixed-layout backend on PDFBox 3.0. Full DSL coverage. |
| DOCX | Partial | Semantic export via Apache POI. Unsupported nodes (`shape`, `line`, `ellipse`, `barcode`) are dropped silently — layout fidelity is best-effort for paragraph / list / table content. |
| DOCX | Partial | Semantic export via Apache POI — paragraphs, block images, tables and metadata. Word owns the flow, so drawing nodes (`shape`, `line`, `ellipse`, `barcode`) are dropped silently. Beyond geometry, **hyperlinks, bookmarks, headers/footers and multi-section documents are not implemented** — see [render-docx](./render-docx/README.md#what-it-maps-and-what-it-does-not). |
| PPTX | Beta | Fixed-layout export via Apache POI from the same resolved layout — one page per editable slide with native shapes and text frames; clipped regions land as pixel-exact pictures. First shipped in 2.1, marked `@Beta` while the API shape settles. |

### Text & internationalization
Expand Down
10 changes: 7 additions & 3 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ it, with **no PDFBox, POI, or template code** on its dependency tree.
## When to depend on it

- You want the smallest engine and will **bring your own render backend** — add
`graph-compose-render-pdf`, or implement the `FixedLayoutBackendProvider` SPI.
`graph-compose-render-pdf`, or implement the `FixedLayoutBackendProvider` SPI. A custom
backend needs **both** halves of the SPI: opening a session measures text through a
`FontMetricsProvider`, so publish one alongside your backend. `graph-compose-render-pdf`
is the only shipped artifact that registers one.
- You are building a library on top of GraphCompose and don't want to impose a backend
on your consumers.

Expand All @@ -27,8 +30,9 @@ try (var doc = GraphCompose.document(out).create()) {
} // with graph-compose-render-pdf present, buildPdf() / toPdfBytes() / toImages() work
```

A core-only classpath asked to render throws `MissingBackendException`, whose message
names the artifact to add.
A core-only classpath throws `MissingBackendException` at `create()` — before any render
call, since the session resolves its font metrics as it opens — and the message names the
artifact to add.

## Install

Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/backend-capability-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Payload records live in `core` under

| Capability (payload) | PDF (fixed) | PPTX (fixed) | DOCX (semantic) |
|---|---|---|---|
| Paragraph — pre-wrapped lines, runs, alignment (`ParagraphFragmentPayload`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` (one absolute, wrap-disabled frame per measured line) | semantic paragraphs (`DocxSemanticBackend`) |
| Paragraph — pre-wrapped lines, runs, alignment (`ParagraphFragmentPayload`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` (one absolute, wrap-disabled frame per measured line) | ⚠️ semantic paragraphs (`DocxSemanticBackend`) — every run takes the paragraph's style, so per-run styling and `linkTarget` are dropped |
| Inline code/badge chips (`InlineBackground` on text spans) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` | ❌ |
| Inline images (`ParagraphImageSpan`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` | ❌ |
| Inline vector shapes (`ParagraphShapeSpan`) | ✅ `PdfParagraphFragmentRenderHandler` | ⚠️ `PptxParagraphFragmentRenderHandler` + `PptxInlineGeometry` (distinct per-corner radii render with the top-left radius — single-adjust preset) | ❌ |
Expand All @@ -64,7 +64,7 @@ Payload records live in `core` under
| Anchor markers (`AnchorMarkerPayload`) | ✅ `PdfAnchorMarkerRenderHandler` + `PdfInternalLinkWriter` | ✅ `PptxAnchorMarkerRenderHandler` + `PptxNavigationWriter` (slide-jump hyperlinks resolved after all fragments, so forward references work) | ❌ |
| Bookmark markers (`BookmarkMarkerPayload`) | ✅ `PdfBookmarkMarkerRenderHandler` + `PdfBookmarkOutlineWriter` | ⚠️ `PptxBookmarkMarkerRenderHandler` + `PptxNavigationWriter` (PPTX has no outline tree — the first bookmark on a page names its slide, further bookmarks on the same page are dropped with a debug note) | ❌ |
| Alpha / opacity | ✅ `PdfAlphaSupport` (`PDExtendedGraphicsState` on every surface — shape fills/strokes, text runs, lines, side borders, table paint) | ✅ native `<a:alpha>` via POI on every surface — fills, strokes, text runs, table paint | ❌ |
| Text decorations — underline / strikethrough (`DocumentTextDecoration`) | ✅ `PdfTextDecorations` (em-proportional marks: underline −0.10 em, strikethrough +0.28 em, thickness 0.05 em) | ✅ `PptxTextFrames.applyStyle` (PowerPoint draws its own marks — sub-point placement differences vs the PDF's constants) | |
| Text decorations — underline / strikethrough (`DocumentTextDecoration`) | ✅ `PdfTextDecorations` (em-proportional marks: underline −0.10 em, strikethrough +0.28 em, thickness 0.05 em) | ✅ `PptxTextFrames.applyStyle` (PowerPoint draws its own marks — sub-point placement differences vs the PDF's constants) | ⚠️ `DocxSemanticBackend.applyStyle` (underline maps to Word's single underline; `STRIKETHROUGH` is dropped) |

## Navigation and interactivity

Expand Down
30 changes: 28 additions & 2 deletions render-docx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,34 @@ try (var doc = GraphCompose.document().create()) {
}
```

DOCX maps the semantic node graph, not the fixed PDF layout — coverage is narrower than the
PDF backend.
## What it maps, and what it does not

DOCX walks the **semantic node graph**, not the fixed PDF layout — Word owns the flow, so
the page geometry the PDF backend resolves is deliberately ignored. Shapes, gradients,
clips, transforms and alpha have no DOCX counterpart by design; a document that draws with
them exports its text and tables, not its drawing.

What maps: paragraphs (runs, alignment), block images (`STRETCH` / `CONTAIN` / `COVER`),
table rows including row and column spans, and document metadata. Run styling carries font
family, size, colour, bold, italic and underline.

Beyond geometry, these are **not implemented** even though Word itself can express them —
check the list before you promise a `.docx` to a reader:

- **Hyperlinks are dropped**, external and internal alike; the link text survives as plain text.
- **No bookmarks and no navigation outline.**
- **No repeating headers or footers**, and no watermark layer.
- **`STRIKETHROUGH` is dropped** — the other decorations map.
- **No barcodes or QR codes**, and no inline images or code/badge chips inside a paragraph.
- **Per-run styling in a mixed-style paragraph is flattened.** Every run in a `RichText`
paragraph is written with the paragraph's style, so a bold or accent-coloured segment
loses its own styling; the text itself is kept.
- **Multi-section documents export through the fixed-layout backends only** —
`renderSections` is not part of the semantic path.
- **Output is not byte-deterministic**: rendering twice does not produce identical files.

Per-capability detail, with the implementing class for every supported cell:
[backend capability matrix](../docs/architecture/backend-capability-matrix.md).

## Install

Expand Down