# SmartScribe.Docx SDK — Complete Architecture Reference
## Session Handoff Document
**Last updated:** 2026-06-21  
**Written for:** Any AI model (or human) picking up this project cold  
**Author of original implementation:** Dan Shields / Gain Publications

---

## 1. What This Is

SmartScribe.Docx is a commercial C++ SDK for reading and writing DOCX files
(ECMA-376 / ISO 29500 Open XML format) with correct spec compliance. It is a
byproduct of building SmartScribe Pro, a professional fiction writing application.

The core differentiator is **correct ECMA-376 implementation**. Virtually all
competing DOCX libraries (LibreOffice UNO, Apache POI, python-docx, Aspose Words)
get the following wrong:

1. **Six-layer style cascade** — correct order is:
   docDefaults → table → numbering → paragraph style (basedOn chain) → character style → direct formatting
2. **Toggle property XOR behavior** — bold/italic applied at run level must XOR
   against the paragraph/style default, not override it
3. **Four-slot rFonts model** — fonts specified per script (ASCII, East Asian,
   Complex Script, High ANSI) and resolved through theme font references
4. **Mirror margins** — must read both sectPr AND settings.xml; neither alone is reliable

SmartScribe.Docx handles all four correctly. This was battle-tested against
The Seal (a full-length novel manuscript) in SmartScribe Pro before release.

The project is completed and commercially ready as of 2026-06-21.

---

## 2. Repository Layout

```
D:\Projects\SmartScribeDocx-SDK\          ← THE SDK PACKAGE (this repo)
├── docs\                                  ← This folder
│   └── ARCHITECTURE.md                   ← This file
├── include\
│   └── SmartScribeDocx.h                 ← THE PUBLIC API (C, 28 functions)
│   └── [internal headers]                ← Source License customers only
├── src\                                  ← Source License customers only
│   ├── SmartScribeDocx.cpp               ← C API + license validation
│   ├── DocxDocument.cpp/h                ← In-memory document model
│   ├── DocxReader.cpp/h                  ← DOCX → document model
│   ├── DocxWriter.cpp/h                  ← Document model → DOCX
│   ├── DocxZip_Qt.cpp                    ← ZIP backend (Qt, default)
│   ├── DocxZip_Miniz.cpp                 ← ZIP backend (Miniz, no-Qt-ZIP)
│   └── Core.cpp/h                        ← Shared utilities
├── third_party\miniz\                    ← MIT license, single-file ZIP lib
├── bindings\
│   ├── python\smartscribe_docx.py        ← ctypes binding, zero compile
│   └── dotnet\SmartScribeDocx.cs         ← P/Invoke binding, .NET 4.5+
├── examples\                             ← Getting started (C, Python, .NET)
├── demos\                                ← Three proof-of-concept programs
│   ├── demo1_roundtrip\                  ← Open DOCX, modify, save
│   ├── demo2_authoring\                  ← Create new DOCX from HTML
│   └── demo3_batch\                      ← Batch normalize a folder
├── tests\capi_test.cpp                   ← 10-test C API test suite
├── prebuilt\win-x64\                     ← Ready-to-use Windows x64 binaries
│   ├── SmartScribeDocx.dll               ← The DLL (debug build)
│   ├── SmartScribeDocx.lib               ← Import library
│   ├── qt.conf                           ← Tells Qt where to find plugins
│   ├── Qt6Cored.dll, Qt6Guid.dll ...     ← Qt runtime (debug)
│   └── platforms\qoffscreend.dll         ← CRITICAL: headless Qt platform
├── private\                              ← NEVER DISTRIBUTE
│   ├── keygen.py                         ← License key generator/verifier
│   └── package.py                        ← Builds distribution zips
├── dist\                                 ← Output of package.py
│   ├── SmartScribeDocx-1.1.0-win-x64.zip
│   └── SmartScribeDocx-1.1.0-win-x64-source.zip
├── README.md
├── CHANGELOG.md
├── LICENSE.txt
└── CMakeLists.txt

D:\Projects\SmartScribe_Qt\               ← SMARTSCRIBE PRO SOURCE
├── SmartScribe.Core\                     ← Same SDK, lives here as source-of-truth
│   ├── include\SmartScribeDocx.h         ← MASTER copy of the header
│   └── src\SmartScribeDocx.cpp           ← MASTER copy of the implementation
└── Bindings\                             ← MASTER copies of language bindings
    ├── Python\smartscribe_docx.py
    └── DotNet\SmartScribeDocx.cs
```

**IMPORTANT:** The master source lives in `D:\Projects\SmartScribe_Qt\SmartScribe.Core\`.
The SDK package contains COPIES. When rebuilding the DLL, always build from
`D:\Projects\SmartScribe_Qt\out\build\debug\` and copy the result to `prebuilt\win-x64\`.

---

## 3. The C API (SmartScribeDocx.h)

Version: **1.1.0**  
DLL export macro: `SSAPI`  
All strings: UTF-8, null-terminated  
All paths: UTF-8, null-terminated  
Calling convention: Cdecl  

### 3.1 Lifecycle

```c
void ssDocx_init(void);
// Initializes Qt headlessly (offscreen platform). Prints development notice.
// Call this OR ssDocx_init_licensed() before any other function.

int ssDocx_init_licensed(const char* key);
// Same as ssDocx_init() but validates a commercial license key.
// Returns 1 if key is valid (no notice). Returns 0 if invalid (notice printed).
// Library functions normally regardless of return value.

const char* ssDocx_version(void);
// Returns "1.1.0"

SsDocxHandle ssDocx_open(const char* utf8_path);
// Opens existing DOCX. Returns NULL on failure.

SsDocxHandle ssDocx_create_blank(const char* utf8_path);
// Creates new empty DOCX at path. Returns NULL on failure.

void ssDocx_close(SsDocxHandle h);
// Frees all resources. Handle is invalid after this call.

int ssDocx_is_valid(SsDocxHandle h);
// 1 = handle is valid, 0 = invalid

const char* ssDocx_error(SsDocxHandle h);
// Returns last error for handle h. Pass NULL for static/init errors.
```

### 3.2 Save

```c
int ssDocx_save(SsDocxHandle h);
// Overwrites the original file.

int ssDocx_save_as(SsDocxHandle h, const char* utf8_path);
// Saves to a new path.
```

### 3.3 Body Content (Authoring)

```c
int ssDocx_set_body_html(SsDocxHandle h, const char* utf8_html);
// Replaces document body from HTML. Does not save — call save() after.
// Supported HTML: <p>, <b>, <i>, <u>, <strong>, <em>, font/size/color spans,
// heading tags <h1>-<h6>, <ul>, <ol>, <li>, hyperlinks, inline images.

int ssDocx_write_html(const char* utf8_path, const char* utf8_html);
// One-shot: create DOCX at path, fill from HTML, save. No handle needed.

int ssDocx_resave_full(const char* in_path, const char* out_path);
// Open existing DOCX, round-trip through the engine, write to out_path.
// Useful for normalization/testing.
```

### 3.4 Margins

All values in **twips** (1 inch = 1440 twips, 1cm ≈ 567 twips).

```c
int ssDocx_get_margins(SsDocxHandle h,
    int* top, int* bottom, int* left, int* right,
    int* header_dist, int* footer_dist, int* gutter, int* mirror_margins);

int ssDocx_set_margins(SsDocxHandle h,
    int top, int bottom, int left, int right,
    int header_dist, int footer_dist, int gutter, int mirror_margins);
// mirror_margins: 0=off, 1=on (swaps left/right on even pages)
```

### 3.5 Headers and Footers

```c
// hf_type constants:
#define SS_HF_DEFAULT  0   // Odd pages (or all pages when even/odd off)
#define SS_HF_EVEN     1   // Even pages (requires evenAndOdd = true)
#define SS_HF_FIRST    2   // First page (requires titlePage = true)

const uint8_t* ssDocx_get_header_xml(SsDocxHandle h, int hf_type, unsigned int* size_out);
const uint8_t* ssDocx_get_footer_xml(SsDocxHandle h, int hf_type, unsigned int* size_out);
// Returns raw OOXML bytes for the header/footer part. NULL if not set.

int ssDocx_set_header_xml(SsDocxHandle h, int hf_type, const uint8_t* xml, unsigned int size);
int ssDocx_set_footer_xml(SsDocxHandle h, int hf_type, const uint8_t* xml, unsigned int size);
// Writes raw OOXML. Content_Types.xml and .rels are updated automatically.

const uint8_t* ssDocx_build_hf_xml(int is_header, const char* template_utf8,
                                    const char* alignment, unsigned int* size_out);
// Builds valid OOXML header/footer from a template string.
// alignment: "left", "center", "right"
// Template variables:
//   {PAGE}      — current page number field
//   {NUMPAGES}  — total pages field
//   {TITLE}     — document title property
//   {CHAPTER}   — chapter heading field (w:styleRef)
//   {AUTHOR}    — document author property
```

### 3.6 Document Properties

```c
const char* ssDocx_get_doc_property(SsDocxHandle h, const char* key);
int ssDocx_set_doc_property(SsDocxHandle h, const char* key, const char* value);
// Keys: "title", "author", "subject", "description", "keywords", "category"
```

### 3.7 Section Flags

```c
int ssDocx_get_title_page(SsDocxHandle h);
int ssDocx_set_title_page(SsDocxHandle h, int on);
// Controls whether the first page uses SS_HF_FIRST header/footer

int ssDocx_get_even_and_odd(SsDocxHandle h);
int ssDocx_set_even_and_odd(SsDocxHandle h, int on);
// Controls whether even pages use SS_HF_EVEN header/footer

int ssDocx_set_page_number_format(SsDocxHandle h, const char* fmt, int start);
// fmt: "decimal", "upperRoman", "lowerRoman", "upperLetter", "lowerLetter"
// start: -1 = continue from previous section, 0+ = explicit start value
```

---

## 4. Qt Dependency

The DLL uses Qt internally for:
- XML parsing (QXmlStreamReader / QXmlStreamWriter)
- ZIP I/O (Qt's bundled zlib OR Miniz — switchable at CMake time)
- DOCX body authoring (QTextDocument / QTextCursor for HTML parsing)
- HMAC-SHA256 for license validation (QMessageAuthenticationCode)

**Qt is completely hidden from callers.** The C API uses only primitive types.

`ssDocx_init()` / `ssDocx_init_licensed()` creates a headless QGuiApplication
on the "offscreen" platform if no Qt application already exists. This is
required because QTextDocument (used for HTML body authoring) needs a GUI app.

**Python/C# callers:** Must have `SmartScribeDocx.dll` and all Qt DLLs in
the same directory OR on the system PATH. The `prebuilt/win-x64/` folder
contains a complete ready-to-use set. The `qt.conf` file tells Qt where to
find its plugins (the `platforms/` subdirectory).

**Python 3.8+ note:** The binding calls `os.add_dll_directory()` on the
DLL's folder so Windows can find the Qt runtime DLLs alongside it.

---

## 5. License System

### 5.1 Tier Structure

| Tier | Name             | Price     | Notes                                        |
|------|------------------|-----------|----------------------------------------------|
| 0    | Development      | Free      | No key. Notice always prints.                |
| 1    | Indie            | Free      | Free registration. <$100k gross/year. Attribution required. |
| 2    | Single App       | $299      | One production app, unlimited deployment.    |
| 3    | Studio           | $899      | Up to 5 apps.                                |
| 4    | OEM / Unlimited  | $2,499    | Unlimited apps.                              |
| 5    | Source License   | $9,999    | Full source. One company, unlimited modify.  |

### 5.2 Key Format

```
SSDX-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX
```

39 characters total. 4 groups of 8 Base32 characters (A-Z, 2-7).

**Payload (20 bytes → 32 Base32 chars):**
- Byte 0: Tier (0-5)
- Bytes 1-3: Maintenance expiry as uint24 big-endian, days since 2026-01-01
  - 0xFFFFFF = perpetual (never expires)
- Bytes 4-11: First 8 bytes of SHA-256(licensee_name_utf8)
- Bytes 12-19: First 8 bytes of HMAC-SHA256(SECRET_KEY, bytes 0-11)

**Validation (in DLL):** XOR two obfuscated byte arrays to recover the
32-byte SECRET_KEY, then recompute the HMAC and compare. No internet access,
no server, works offline and in air-gapped environments forever.

### 5.3 Key Generation

```bash
python private/keygen.py generate --tier 2 --licensee "Acme Corp" --years 1
python private/keygen.py generate --tier 1 --licensee "Indie Dev" --years 1
python private/keygen.py generate --tier 4 --licensee "BigCo Inc" --years 0  # perpetual
python private/keygen.py verify SSDX-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX
python private/keygen.py gen-cpp  # print new C++ XOR constants
```

### 5.4 Notice Behavior

| Status                      | ssDocx_init_licensed() returns | stderr output                        |
|-----------------------------|-------------------------------|--------------------------------------|
| Valid commercial key        | 1                             | None                                 |
| Valid Indie key             | 1                             | None                                 |
| Expired commercial maint.   | 1 (still works)               | "maintenance expired" notice         |
| Expired Indie registration  | 1 (still works)               | "re-register free" notice            |
| No key / invalid key        | 0 (still works)               | "Development License" notice         |

**The library always works regardless of license status.**
The notice is informational only — it prints to stderr, not stdout,
and never blocks or cripples the API.

### 5.5 Changing the Secret Key

1. Generate new key: `python -c "import os,binascii; print(binascii.hexlify(os.urandom(32)).decode())"`
2. Edit `SECRET_KEY` in `private/keygen.py`
3. Run `python private/keygen.py gen-cpp`
4. Paste output into `SmartScribe.Core/src/SmartScribeDocx.cpp` (kSecretMask / kSecretXor)
5. Rebuild DLL
6. **All existing keys will be invalid against the new DLL.**
   Customers on perpetual licenses need new keys issued to them.

---

## 6. Building the DLL

**Environment:** Windows 10/11, Visual Studio 2022, Qt 6.10.2 MSVC x64

```powershell
# From VS Developer PowerShell:
cd D:\Projects\SmartScribe_Qt\out\build\debug
ninja SmartScribeDocx

# Output: D:\Projects\SmartScribe_Qt\out\build\debug\SmartScribeDocx.dll
```

After rebuilding, copy to SDK package:
```powershell
Copy-Item "D:\Projects\SmartScribe_Qt\out\build\debug\SmartScribeDocx.dll" `
          "D:\Projects\SmartScribeDocx-SDK\prebuilt\win-x64\" -Force
```

**Note:** The prebuilt DLL is a DEBUG build. For customer distribution,
a Release configuration should be built to reduce size and improve performance.
Release build removes the `d` suffix from Qt DLLs (Qt6Core.dll vs Qt6Cored.dll).
Run `windeployqt` again against the Release DLL to get the correct release Qt files.

---

## 7. Building Distribution Packages

```powershell
python private/package.py                  # builds both packages
python private/package.py --tier standard  # Tier 0-4 only
python private/package.py --tier source    # Tier 5 only
python private/package.py --version 1.2.0  # different version number
```

Output goes to `dist/`:
- `SmartScribeDocx-1.1.0-win-x64.zip` — for Tier 0-4 customers (24.3 MB)
- `SmartScribeDocx-1.1.0-win-x64-source.zip` — for Tier 5 customers (24.4 MB)

Source License customers get everything in the standard package PLUS all .cpp
source files, all internal headers, CMakeLists.txt, and test suite.

---

## 8. Language Bindings

### 8.1 Python (`bindings/python/smartscribe_docx.py`)

Zero compile. Python 3.7+. Uses ctypes.

```python
import smartscribe_docx as ss

# Option A: development (notice prints)
# Just import and use — init is called automatically on first DocxDocument

# Option B: production (no notice)
ss.init_licensed("SSDX-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX")

with ss.DocxDocument("input.docx") as doc:
    doc.set_margins(1440, 1440, 1440, 1440)
    ftr = ss.DocxDocument.build_hf_xml(False, "Page {PAGE} of {NUMPAGES}", "center")
    doc.set_footer_xml(0, ftr)
    doc.save_as("output.docx")

# One-shot authoring:
ss.DocxDocument.write_html("output.docx", "<h1>Title</h1><p>Body text.</p>")
ss.DocxDocument.resave_full("input.docx", "normalized.docx")
```

The binding automatically calls `os.add_dll_directory()` on Python 3.8+ to
ensure Qt DLLs alongside SmartScribeDocx.dll are findable by Windows.

DLL search order:
1. Same folder as smartscribe_docx.py
2. Parent folder of smartscribe_docx.py
3. `SMARTSCRIBEDOCX_DIR` environment variable
4. System PATH (ctypes.util.find_library)

### 8.2 .NET (`bindings/dotnet/SmartScribeDocx.cs`)

Single file. .NET 4.5+ and .NET 6+. P/Invoke. No NuGet package required.

```csharp
using SmartScribe;

// Option A: development
// new DocxDocument() triggers ssDocx_init() automatically via static constructor

// Option B: production
DocxDocument.InitLicensed("SSDX-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX");

using var doc = new DocxDocument("input.docx");
doc.SetMargins(new DocxMargins { Top=1440, Bottom=1440, Left=1440, Right=1440 });
var ftr = DocxDocument.BuildHfXml(isHeader: false, "Page {PAGE} of {NUMPAGES}", "center");
doc.SetFooterXml(HfType.Default, ftr);
doc.SaveAs("output.docx");

// One-shot authoring:
DocxDocument.WriteHtml("output.docx", "<h1>Title</h1><p>Body text.</p>");
DocxDocument.ResaveFull("input.docx", "normalized.docx");
```

HTML strings use `byte[]` / `ToUtf8()` internally to avoid `LPUTF8Str`
which is .NET 7+ only. The `ToUtf8()` helper manually encodes to UTF-8
with a null terminator, compatible with all .NET versions.

---

## 9. Known Issues and Limitations (as of v1.1.0)

1. **Debug DLL in prebuilt/** — `d`-suffix Qt DLLs indicate a debug build.
   Fine for development, should be replaced with Release for production shipping.

2. **Windows only currently** — The SDK builds on Windows with MSVC.
   Linux/macOS builds are possible (Qt and Miniz are cross-platform) but
   have not been set up. A GitHub Actions CI pipeline would enable this.

3. **Qt required at runtime** — The DLL bundles Qt. The `prebuilt/win-x64/`
   folder contains the complete Qt runtime needed. Customers who cannot
   ship Qt DLLs should use the Source License and explore a path to
   replacing the Qt XML dependency with pugixml or similar.

4. **No NuGet package** — The .NET binding is a single .cs file that customers
   drop into their project. A proper NuGet package would improve discoverability.

5. **No PyPI package** — The Python binding is a single .py file. Publishing
   to PyPI as `pip install smartscribe-docx` would improve discoverability.

6. **`ssDocx_build_hf_xml` builds single-run paragraphs** — The builder
   creates headers/footers with a single paragraph and single alignment.
   Complex multi-zone headers (left/center/right in one header) require
   the caller to supply raw OOXML via set_header_xml / set_footer_xml.

---

## 10. Pricing and Distribution (as of 2026-06-21)

**Payment processor:** Paddle (handles VAT/GST internationally)
**Product page:** https://smartscribepro.com/docx-sdk (to be built)

**Indie tier registration:** Fill-out form → automatic email with Tier 1 key.
The registration is a binding legal agreement capping usage at <$100k gross/year.
The form captures: name, email, project name, URL, agreement checkbox.
Annually renewable — free each year, keeps your mailing list current.

**Attribution requirement (Indie only):** "Built with SmartScribe.Docx"
in About box or documentation. Not a visible watermark in documents.

---

## 11. Next Steps

- [ ] Build a Release configuration of the DLL (not debug)
- [ ] Set up product page with Paddle integration for Tier 2-5 purchases
- [ ] Set up Indie registration form with automated key email
- [ ] Publish Python binding to PyPI
- [ ] Create NuGet package for .NET binding
- [ ] GitHub repository for SDK (public: include/, bindings/, examples/, docs/)
- [ ] CI/CD pipeline for Linux and macOS builds
- [ ] API reference documentation (Doxygen or hand-written)
- [ ] Migration guide for Aspose Words and python-docx users
