PHPackages                             dskripchenko/php-docx - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PDF &amp; Document Generation](/categories/documents)
4. /
5. dskripchenko/php-docx

ActiveLibrary[PDF &amp; Document Generation](/categories/documents)

dskripchenko/php-docx
=====================

Pure-PHP DOCX (OOXML) library: bidirectional HTML ↔ DOCX conversion, full styles cascade, tables with vMerge/gridSpan, lists, images, headers/footers, watermarks, fields, bookmarks. No external dependencies.

v1.0.0(3w ago)01MITPHPPHP ^8.2

Since May 13Pushed 3w agoCompare

[ Source](https://github.com/dskripchenko/php-docx)[ Packagist](https://packagist.org/packages/dskripchenko/php-docx)[ RSS](/packages/dskripchenko-php-docx/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (2)Used By (0)

dskripchenko/php-docx
=====================

[](#dskripchenkophp-docx)

Pure-PHP DOCX (Office Open XML) library: **bidirectional HTML ↔ DOCX**conversion, **fluent programmatic builder**, **variable detection**, **round-trip-safe AST**. No external dependencies beyond standard PHP extensions.

**Read this in other languages:****English** · [Русский](docs/ru.md) · [中文](docs/zh.md) · [Deutsch](docs/de.md)

---

Table of contents
-----------------

[](#table-of-contents)

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick start](#quick-start)
    - [HTML → DOCX](#1-html--docx)
    - [Programmatic builder](#2-programmatic-builder)
    - [DOCX → HTML / AST](#3-docx--html--ast)
- [HTML → DOCX](#html--docx-1)
- [Programmatic builder API](#programmatic-builder-api)
- [DOCX → HTML (Reader)](#docx--html-reader)
- [Headers, footers &amp; watermarks](#headers-footers--watermarks)
- [Variable detection](#variable-detection)
- [Length helpers](#length-helpers)
- [AST overview](#ast-overview)
- [Round-trip](#round-trip)
- [Architecture](#architecture)
- [Development](#development)
- [License](#license)

---

Features
--------

[](#features)

- **HTML → DOCX writer** — full set of typical layout elements (paragraphs/headings/tables/lists/images/links/fields), inline-style resolution, custom heading registry.
- **DOCX → HTML reader** — parses arbitrary Word/Pages/LibreOffice documents into a typed AST, then serialises back to HTML with inline styles. Style cascade (docDefaults → named → direct), theme colors, numbering reconstruction, vMerge/gridSpan collapse, watermark detection (VML + DrawingML).
- **Fluent programmatic builder** — `DocumentBuilder` with closure scopes for nested structures (tables, lists, headers).
- **Variable detection** — MERGEFIELD, SDT content controls, configurable text patterns (`{{x}}`, `${x}`, `%x%`).
- **Multi-header/footer** — default / first-page / even-pages variants with automatic `` and `` plumbing.
- **Round-trip safe** — read DOCX → AST → write DOCX produces a valid document; bytes-level differences are limited to whitespace/ordering.
- **PHP 8.2+** — `readonly` value-objects, named arguments, constructor promotion, enums.
- **Zero composer dependencies.**

### Out of scope

[](#out-of-scope)

Tracked changes, comments, embedded charts, OLE objects, footnotes/endnotes, SmartArt, math equations (OMML), form fields, custom XML parts.

---

Requirements
------------

[](#requirements)

- PHP **8.2+**
- `ext-zip`, `ext-dom`, `ext-mbstring`

---

Installation
------------

[](#installation)

```
composer require dskripchenko/php-docx
```

---

Quick start
-----------

[](#quick-start)

### 1. HTML → DOCX

[](#1-html--docx)

```
use Dskripchenko\PhpDocx\Html\Converter;
use Dskripchenko\PhpDocx\Writer\Word2007Writer;

$html = write($doc));
```

### 2. Programmatic builder

[](#2-programmatic-builder)

```
use Dskripchenko\PhpDocx\Build\DocumentBuilder;
use Dskripchenko\PhpDocx\Element\ListFormat;

DocumentBuilder::new()
    ->watermark('DRAFT')
    ->header(fn ($h) => $h->paragraph('Acme Inc.'))
    ->footer(fn ($f) => $f->paragraph(fn ($p) => $p
        ->text('Page ')->pageNumber()->text(' of ')->totalPages()
    ))
    ->heading(1, 'Invoice #42')
    ->paragraph(fn ($p) => $p
        ->text('Customer: ')->bold('Acme Co.')
        ->lineBreak()
        ->text('ID: ')->mergeField('CustomerID')
    )
    ->table(fn ($t) => $t
        ->columns(fn ($c) => $c->widthCm(8), fn ($c) => $c->widthCm(3))
        ->headerRow(['Item', 'Qty'])
        ->row(['Widget', '2'])
    )
    ->orderedList(fn ($l) => $l
        ->format(ListFormat::LowerLetter)
        ->item('Net 30 terms')
        ->item('Free shipping')
    )
    ->toFile('invoice.docx');
```

### 3. DOCX → HTML / AST

[](#3-docx--html--ast)

```
use Dskripchenko\PhpDocx\Reader\DocxReader;
use Dskripchenko\PhpDocx\Reader\DocxPackageReader;
use Dskripchenko\PhpDocx\Reader\VariableDetector;
use Dskripchenko\PhpDocx\Html\Serializer;

$bytes = file_get_contents('input.docx');

$document = (new DocxReader)->read($bytes);

$pkg = (new DocxPackageReader)->read($bytes);
$variables = (new VariableDetector)->detect($pkg);

$imported = (new Serializer)->serialize($document, $variables);

echo $imported->bodyHtml;
echo $imported->headerHtml;
echo $imported->footerHtml;
echo $imported->watermarkText;
$imported->pageSettings;
$imported->variables;
$imported->media;
```

---

HTML → DOCX
-----------

[](#html--docx)

Input HTML must use **inline styles only** (no `` blocks). Use a CSS-inliner upstream if needed.

### Supported elements

[](#supported-elements)

CategoryHTML tagsText blocks``, ``, ``, ``, ``Inline marks`/`, `/`, ``, `/`, ``, ``, ``Code/teletype``, ``, ``, ``, ``, ``, ``, ``Links`` external, `` internal, `` bookmarksImages``Tables``, `/`, ``, `/`, `/`, ``, `colspan`, `rowspan`Lists``, ``, ``, `//`Custom tags``, ``, ``, ``Layout``, ``, `/`### Inline styles

[](#inline-styles)

The converter understands `style="…"` properties:

- Run-level: `font-family`, `font-size`, `font-weight`, `font-style`, `text-decoration`, `color`, `background-color`
- Paragraph-level: `text-align`, `margin`, `text-indent`, `line-height`, `border`, `padding`
- Table-level: `width`, `border`, `border-collapse`
- Cell-level: `width`, `padding`, `border`, `vertical-align`, `background-color`

### Custom tags

[](#custom-tags)

```
Page  of
Generated on
```

These become OOXML field codes (``).

### Custom heading styles

[](#custom-heading-styles)

```
use Dskripchenko\PhpDocx\Style\StyleRegistry;
use Dskripchenko\PhpDocx\Style\RunStyle;
use Dskripchenko\PhpDocx\Style\ParagraphStyle;
use Dskripchenko\PhpDocx\Style\Alignment;

$styles = (new StyleRegistry)
    ->heading(1, new RunStyle(sizeHalfPoints: 44, bold: true), new ParagraphStyle(alignment: Alignment::Center))
    ->heading(2, new RunStyle(sizeHalfPoints: 28, bold: true));

$writer = new Word2007Writer($styles);
```

---

Programmatic builder API
------------------------

[](#programmatic-builder-api)

The `Build` namespace provides a fluent API for assembling DOCX documents block by block, finalising to the same immutable AST that the HTML pipeline produces.

### DocumentBuilder

[](#documentbuilder)

Entry point. Accumulates body, header/footer, watermark, page setup.

```
use Dskripchenko\PhpDocx\Build\DocumentBuilder;
use Dskripchenko\PhpDocx\Style\PageSetup;
use Dskripchenko\PhpDocx\Style\PaperSize;
use Dskripchenko\PhpDocx\Style\Orientation;

$doc = DocumentBuilder::new()
    ->pageSetup(new PageSetup(
        paperSize: PaperSize::A4,
        orientation: Orientation::Portrait,
    ))
    ->watermark('CONFIDENTIAL')
    ->heading(1, 'Report')
    ->paragraph('Body')
    ->build();           // → Document AST

$bytes = DocumentBuilder::new()->paragraph('Hi')->toBytes();
$count = DocumentBuilder::new()->paragraph('Hi')->toFile('out.docx');
```

### ParagraphBuilder

[](#paragraphbuilder)

Inside `->paragraph(fn ($p) => …)`:

```
->paragraph(fn ($p) => $p
    ->text('plain ')
    ->bold('bold ')
    ->italic('italic ')
    ->underline('under ')
    ->strike('strike ')
    ->sup('super')->text('script ')
    ->sub('sub')->text('script ')
    ->styled('red', fn ($s) => $s->color('ff0000')->bold())
    ->lineBreak()
    ->link('https://example.com', 'website')
    ->internalLink('section1', 'go to section 1')
    ->bookmark('anchor1', 'anchor target')
    ->pageNumber()
    ->totalPages()
    ->currentDate('yyyy-MM-dd')
    ->mergeField('CustomerName')
    ->image($img)
    ->imageFromFile('/path/to/logo.png', widthPx: 150, altText: 'Logo')
)
```

Paragraph-level styling:

```
->paragraph(fn ($p) => $p
    ->alignCenter()           // or alignRight()/alignJustify()
    ->indentMm(left: 20, firstLine: 10)
    ->spacingPt(before: 6, after: 12)
    ->text('Indented & spaced')
)
```

### TableBuilder

[](#tablebuilder)

```
use Dskripchenko\PhpDocx\Build\{TableBuilder, TableRowBuilder, TableCellBuilder, ColumnBuilder};

->table(fn (TableBuilder $t) => $t
    ->caption('Sales 2026')
    ->column(fn (ColumnBuilder $c) => $c->widthCm(6))
    ->column(fn (ColumnBuilder $c) => $c->widthCm(3))
    ->widthPercent(100)
    ->alignCenter()
    ->cellMarginsMm(2)
    ->headerRow(['Item', 'Price'])
    ->row(['Apple', '10 USD'])
    ->row(fn (TableRowBuilder $r) => $r
        ->cell('Banana')
        ->cell(fn (TableCellBuilder $c) => $c
            ->backgroundColor('ffeb3b')
            ->valignCenter()
            ->paragraph(fn ($p) => $p->bold('20 USD'))
        )
    )
)
```

Spans and merges:

```
->row(fn ($r) => $r
    ->cell(fn ($c) => $c->gridSpan(2)->paragraph('Wide header'))
)
->row(fn ($r) => $r
    ->cell(fn ($c) => $c->rowSpan(2)->paragraph('Tall'))
    ->cell('right')
)
```

### ListBuilder

[](#listbuilder)

```
use Dskripchenko\PhpDocx\Build\ListBuilder;
use Dskripchenko\PhpDocx\Element\ListFormat;

->bulletList(fn (ListBuilder $l) => $l
    ->item('First')
    ->item('Second', fn ($n) => $n
        ->item('Nested A')
        ->item('Nested B')
    )
)

->orderedList(fn (ListBuilder $l) => $l
    ->format(ListFormat::LowerLetter)   // a, b, c
    ->startAt(3)
    ->item('item starts at "c"')
)
```

### RunStyleBuilder

[](#runstylebuilder)

Used inside `->styled(text, fn (RunStyleBuilder) => …)` or standalone via `RunStyleBuilder::new()->…->build()`.

```
RunStyleBuilder::new()
    ->bold()
    ->italic()
    ->underline()
    ->strike()
    ->color('ff0000')
    ->backgroundColor('eeeeee')
    ->highlight('yellow')
    ->fontFamily('Arial')
    ->fontSizePt(14.5)
    ->build();
```

### Length helpers

[](#length-helpers)

Convert common units to OOXML twips (1 twip = 1/20 pt). Used wherever a twip int is expected.

```
use Dskripchenko\PhpDocx\Build\Length;

Length::pt(12);     // 240
Length::mm(20);     // 1134
Length::cm(2.5);    // 1417
Length::inch(0.5);  // 720
Length::px(100);    // 1500  (CSS px @ 96 DPI)
```

Most builders expose unit-aware shortcuts:

- TableBuilder: `widthPt/widthMm/widthCm/widthInches`, `cellMarginsMm/cellMarginsPt`
- TableCellBuilder: `widthPt/Mm/Cm/Inches`, `paddingMm/Pt/Cm/Inches`
- ColumnBuilder: `widthPt/Mm/Cm/Inches/Px`
- ParagraphBuilder: `indentMm/Cm/Pt/Inches`, `spacingPt/Mm`
- RunStyleBuilder: `fontSizePt`

---

DOCX → HTML (Reader)
--------------------

[](#docx--html-reader)

### High-level: DocxReader

[](#high-level-docxreader)

```
use Dskripchenko\PhpDocx\Reader\DocxReader;

$document = (new DocxReader)->read(file_get_contents('input.docx'));
// → Document (AST)
```

This runs the full pipeline: package unpack → styles resolve → body/header/footer parsing → vMerge/list reconstruction → image extraction → watermark detection → page setup.

### Low-level: DocxPackageReader

[](#low-level-docxpackagereader)

If you need the raw OOXML parts:

```
use Dskripchenko\PhpDocx\Reader\DocxPackageReader;

$pkg = (new DocxPackageReader)->read($bytes);

$pkg->documentXml;           // \DOMDocument
$pkg->stylesXml;             // ?\DOMDocument
$pkg->numberingXml;          // ?\DOMDocument
$pkg->themeXml;              // ?\DOMDocument
$pkg->settingsXml;           // ?\DOMDocument
$pkg->headers;               // array
$pkg->footers;               // array
$pkg->media;                 // array
$pkg->documentRelationships();  // list
$pkg->resolveDocumentRel('rId7');  // Relationship
```

### Serializer: AST → HTML

[](#serializer-ast--html)

```
use Dskripchenko\PhpDocx\Html\Serializer;

$imported = (new Serializer)->serialize($document, $variables);

// ImportedDocument:
$imported->bodyHtml;         // string
$imported->headerHtml;       // ?string
$imported->footerHtml;       // ?string
$imported->watermarkText;    // ?string
$imported->pageSettings;     // PageSetup
$imported->variables;        // list
$imported->media;            // array
```

HTML output uses inline styles only — re-loadable into the same library via `Html\Converter::fromHtml($imported->bodyHtml)`.

---

Headers, footers &amp; watermarks
---------------------------------

[](#headers-footers--watermarks)

Three header/footer types are supported per section: `default`, `first`(title page), `even` (even pages). Word automatically renders the right one based on page number.

```
DocumentBuilder::new()
    ->header(fn ($h) => $h->paragraph('Default header'))
    ->firstHeader(fn ($h) => $h->paragraph('Cover page'))
    ->evenHeader(fn ($h) => $h->paragraph(fn ($p) => $p
        ->text('Page ')->pageNumber()
    ))
    ->footer(fn ($f) => $f->paragraph('© 2026 Acme'))
    ->firstFooter(fn ($f) => $f->paragraph('Confidential'))
    ->evenFooter(fn ($f) => $f->paragraph('Even footer'))
    ->paragraph('Body')
    ->toFile('with-headers.docx');
```

The writer automatically:

- emits `` in `sectPr` when first-page header/footer is set
- emits `word/settings.xml` with `` when even header/footer is set

### Watermark

[](#watermark)

```
DocumentBuilder::new()
    ->watermark('DRAFT')
    ->paragraph('Body')
    ->toFile('with-watermark.docx');
```

Renders as a 45°-rotated VML text shape on every page.

---

Variable detection
------------------

[](#variable-detection)

Scans an imported DOCX for three kinds of variables:

1. **MERGEFIELD** — Word mail-merge native, both simple ``and complex `` form.
2. **SDT content controls** — `` with ``.
3. **Text patterns** — configurable regexes (defaults: `{{name}}`, `${name}`, `%name%`).

```
use Dskripchenko\PhpDocx\Reader\VariableDetector;

$pkg = (new DocxPackageReader)->read($bytes);
$detector = new VariableDetector;     // defaults
// Or with custom regexes:
$detector = new VariableDetector(['/\[\[(\w+)\]\]/']);

$variables = $detector->detect($pkg);
foreach ($variables as $v) {
    echo "{$v->name} ({$v->source->value})";
    echo " placeholder='{$v->placeholder}'";
    echo " sample='{$v->sampleValue}'\n";
}
```

Detection runs across `body + all headers + all footers`. Results are deduplicated by `(source, name)`.

---

Length helpers
--------------

[](#length-helpers-1)

See [Length helpers](#length-helpers) above. Conversion table:

UnitTwipsPtNotes1 twip10.05OOXML native1 pt201typography1 mm~572.83metric1 cm~56728.35metric1 inch144072imperial1 px150.75CSS @ 96 DPI---

AST overview
------------

[](#ast-overview)

All elements live under `Dskripchenko\PhpDocx\Element` namespace.

ElementTypeNotes`Document`root`{ section: Section, watermarkText: ?string }``Section`container`{ body, header, footer, pageSetup, firstHeader, firstFooter, evenHeader, evenFooter }``Paragraph`BlockElement`{ children: InlineElement[], style: ParagraphStyle, headingLevel: ?int }``Run`InlineElement`{ text: string, style: RunStyle }``Hyperlink`InlineElement`{ href: ?string, anchor: ?string, children: InlineElement[] }``Bookmark`InlineElement`{ name: string, children: InlineElement[] }``Image`both`{ binary, format, widthEmu, heightEmu, altText }``Field`InlineElement`{ instruction: string, style: RunStyle }``LineBreak`, `PageBreak`, `HorizontalRule`bothmarker elements`Table`BlockElement`{ rows: TableRow[], style, caption, gridColumnsTwips }``TableRow`element`{ cells: TableCell[], isHeader, heightTwips }``TableCell`element`{ children: BlockElement[], style: CellStyle }``ListNode`BlockElement`{ items: ListItem[], ordered, format, startAt }``ListItem`element`{ children: InlineElement[], nestedList: ?ListNode }`Styles live under `Dskripchenko\PhpDocx\Style`:

- `RunStyle` — font, weight, italic, color, size, highlight, …
- `ParagraphStyle` — alignment, indents, spacing, borders
- `CellStyle` — width, padding, borders, valign, gridSpan, rowSpan
- `TableStyle` — width, borders, alignment, cell margins, layout
- `PageSetup`, `PaperSize`, `Orientation`, `Alignment`, `VerticalAlign`, `BorderStyle`, `Border`, `BorderSet`

---

Round-trip
----------

[](#round-trip)

```
$bytes1 = file_get_contents('original.docx');
$doc = (new DocxReader)->read($bytes1);
$bytes2 = (new Word2007Writer)->write($doc);
file_put_contents('roundtrip.docx', $bytes2);
```

The library targets **semantic** round-trip safety, not byte equality — content, structure and styling survive, but XML ordering and whitespace may differ.

In-scope round-trip features:

- Paragraphs/headings with all run formatting
- Tables with `vMerge`/`gridSpan` reconstruction
- Lists (bullet/decimal/letter/roman) with arbitrary nesting
- Images with EMU sizes and alt text
- Hyperlinks (external + internal anchors) and bookmarks
- Headers/footers (default/first/even) and watermarks
- Field codes (PAGE, NUMPAGES, DATE, MERGEFIELD)
- Page setup (size, orientation, margins)

Out-of-scope features are silently dropped (footnotes, comments, equations, etc.).

---

Architecture
------------

[](#architecture)

```
HTML (inline styles)
       │
       ▼  Html\Converter
   Document (AST)  ◀──── DocumentBuilder (programmatic)
       │
       ▼  Writer\Word2007Writer
   DOCX bytes
       ▲
       │  Reader\DocxReader
   Document (AST)
       │
       ▼  Html\Serializer
   ImportedDocument (bodyHtml, headerHtml, footerHtml, variables, media)

```

The same `Document` AST is shared by HTML conversion, programmatic construction and DOCX reading — every entry/exit point operates on typed value-objects.

---

Development
-----------

[](#development)

```
composer install
composer test       # phpunit suite (~340 tests)
composer stan       # phpstan level 8
```

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance94

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

27d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3aae29ece4986be5d3eea1ba57457547d2ce92fcdee466af45f69ec4079ec9c7?d=identicon)[dskripchenko](/maintainers/dskripchenko)

---

Tags

worddocxOOXMLHTML to DOCXdocx-to-htmldocx-parserdocx-readerdocx-writer

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/dskripchenko-php-docx/health.svg)

```
[![Health](https://phpackages.com/badges/dskripchenko-php-docx/health.svg)](https://phpackages.com/packages/dskripchenko-php-docx)
```

###  Alternatives

[phpoffice/phpword

PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)

7.6k37.4M218](/packages/phpoffice-phpword)[gotenberg/gotenberg-php

A PHP client for interacting with Gotenberg, a developer-friendly API for converting numerous document formats into PDF files, and more!

3835.9M26](/packages/gotenberg-gotenberg-php)[mnvx/lowrapper

PHP wrapper over LibreOffice converter

127199.1k](/packages/mnvx-lowrapper)[aspose-cloud/aspose-words-cloud

Open, generate, edit, split, merge, compare and convert Word documents. Integrate Cloud API into your solutions to manipulate documents. Convert PDF to Word (DOC, DOCX, ODT, RTF and HTML) and in the opposite direction.

34169.4k](/packages/aspose-cloud-aspose-words-cloud)[novay/laravel-word-template

Package Laravel untuk melakukan penggantian kata pada file menggunakan template dokumen (.doc atau .docx) yang sudah disediakan.

5515.9k](/packages/novay-laravel-word-template)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
