Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
634d1ed
test(office): define DOCX page layout RED
seonghobae Aug 10, 2026
d839c36
feat(office): implement bounded DOCX page layout
seonghobae Aug 10, 2026
1994055
feat(office): apply bounded DOCX page layout
seonghobae Aug 10, 2026
2abbe94
feat(office): expose bounded DOCX page layout schema
seonghobae Aug 10, 2026
e2649c9
test(office): bind DOCX page layout schema and determinism
seonghobae Aug 10, 2026
861ba66
test(office): cover DOCX page layout boundaries
seonghobae Aug 10, 2026
88c7f19
test(office): require fail-closed page layout publication
seonghobae Aug 10, 2026
d7dd7bf
Merge protected main into DOCX page layout feature
seonghobae Aug 10, 2026
529e5fc
docs(office): document bounded DOCX page layout decision
seonghobae Aug 10, 2026
d5fd258
fix(docs): satisfy ADR rollback quality contract
seonghobae Aug 10, 2026
874d8a1
test(office): preserve hyperlink relationships through page layout
seonghobae Aug 10, 2026
60fab66
docs(office): document bounded DOCX page layout
seonghobae Aug 10, 2026
76b0573
test(office): preserve image accessibility through page layout
seonghobae Aug 10, 2026
16fffea
test(docs): keep page-layout buyer guide discoverable
seonghobae Aug 10, 2026
93bb824
docs(office): record page-layout image fidelity evidence
seonghobae Aug 10, 2026
2ded31d
test(office): cover full DOCX page layout matrix
seonghobae Aug 10, 2026
34a45a2
test(office): preserve page breaks through layout pass
seonghobae Aug 10, 2026
e49a51a
test(office): enforce DOCX-only page layout boundary
seonghobae Aug 10, 2026
6415e5b
docs(traceability): map proposed DOCX page layout evidence
seonghobae Aug 10, 2026
f63c887
test(office): preserve DOCX structure through layout pass
seonghobae Aug 10, 2026
563ad08
test(docs): enforce page layout traceability status
seonghobae Aug 10, 2026
7038373
test(office): preserve default layout when omitted
seonghobae Aug 10, 2026
c90a7b4
test(office): verify page layout OOXML semantics
seonghobae Aug 10, 2026
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
7 changes: 6 additions & 1 deletion docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This record maps durable Inkspan product decisions to authoritative standards, p
| DOCX bounded paragraph alignment | `paragraph` and `rich_paragraph` optionally preserve exact `left`, `center`, `right`, or `justify`; omission preserves inherited/default Word alignment | Microsoft WordprocessingML paragraph documentation; python-docx paragraph API | protected-main #130 renderer/schema/tests, ADR 0024, Office guide and paragraph-alignment doctoring | Alignment is bounded to the protected paragraph contract; list/table/title/page-layout/style authority is not implied |
| DOCX bounded heading alignment | `heading` optionally preserves the same exact `left`, `center`, `right`, or `justify` contract through the shared paragraph-alignment mapping; omission preserves inherited/default heading-style alignment | Microsoft WordprocessingML paragraph documentation; python-docx paragraph API | protected-main #134 renderer/schema/tests, ADR 0025, Office guide and heading-alignment doctoring | Heading alignment adds no arbitrary heading style, outline numbering, TOC, list/table/title/page-layout, source-format, network, model, credential or persistence authority |
| DOCX bounded external hyperlinks | `rich_paragraph.runs[]` optionally preserves one exact bounded printable-ASCII absolute HTTP(S) target as a relationship-backed external `w:hyperlink` while retaining visible Unicode text and run emphasis | ECMA-376 Office Open XML package/WordprocessingML semantics; Microsoft Open XML hyperlink/relationship documentation; python-docx 1.2.0 hyperlink feature analysis | protected-main #137 renderer/schema/OOXML tests, ADR 0026 and Office guidance | Accepted syntax is not destination trust; Inkspan performs no fetch, DNS, redirect, local-file read, credential use, tenant policy, phishing policy, internationalized-URI conversion, persistence, or distribution |
| DOCX bounded single-section page layout | Proposed `page_layout` accepts only A4/Letter, portrait/landscape, and complete integer 0–100 mm edge margins; it applies only to the existing sole DOCX section and is canonicalized after the layout pass | ECMA-376 Office Open XML section/page semantics; python-docx 1.2.0 section API and section guidance | active PR #141 schema/renderer/page-layout tests, ADR 0027, `docs/docx-page-layout.md`, page-layout doctoring, all-four paper/orientation round trips, page-break/hyperlink/image fidelity and DOCX-only format-boundary tests | `implemented_on_active_pr`; not protected-main or released authority, and no arbitrary page geometry, multiple sections, headers/footers, printer/PDF service, persistence, auth, network, model, credential, or host policy authority is implied |
| Release authority | Source movement invalidates exact-head evidence; stale assets/digest ambiguity fail closed; formal approval remains distinct from status/comments | GitHub protected-branch/review/release/attestation behavior and repository policy | release workflow tests, package checksums, browser evidence, SBOM/provenance, formal reviews | Local success or automated prose does not authorize protected merge/release |

## Current primary references
Expand Down Expand Up @@ -66,6 +67,10 @@ python-docx. (n.d.-a). *Hyperlink — python-docx 1.2.0 documentation*. Retrieve

python-docx. (n.d.-b). *Working with text*. Retrieved August 10, 2026, from https://python-docx.readthedocs.io/en/latest/user/text.html

python-docx contributors. (2025). *Working with sections (python-docx 1.2.0 documentation).* https://python-docx.readthedocs.io/en/latest/user/sections.html

python-docx contributors. (2025). *Section objects (python-docx 1.2.0 documentation).* https://python-docx.readthedocs.io/en/stable/api/section.html

Rundgren, A., Jordan, B., & Erdtman, S. (2020). *JSON Canonicalization Scheme (JCS)* (RFC 8785). RFC Editor. https://doi.org/10.17487/RFC8785

Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software Development Framework (SSDF) Version 1.1: Recommendations for Mitigating the Risk of Software Vulnerabilities* (NIST SP 800-218). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218
Expand Down Expand Up @@ -106,7 +111,7 @@ Lower levels may explain intent or history but cannot override a contradictory h
- **Planned** means an accepted future direction without a protected implementation.
- **Superseded** means retained for history but replaced by a later explicit decision.

Envelope identity routing, SafeClipboard, W3C text-position selector evidence, cross-engine browser assurance, headless deterministic Markdown serialization, CSS paged-media output, accessible placeholder semantics, DOCX informative PNG figures, bounded rich-text runs, bounded paragraph alignment, bounded heading alignment, and bounded external hyperlinks are implemented on protected `main`. The W3C selector remains revision-scoped and projection-version-scoped and must satisfy `0 <= start <= end <= projectedCodePointLength`; protected integration does not transfer annotation persistence, source identity, authorization, or re-anchoring authority from the host. The browser gate being protected does not let a future release reuse historical browser evidence: the exact release candidate must generate fresh evidence bound to its own source, committed synthetic corpus, package-lock SHA-256, run identity, browser revisions, and packed npm artifact SHA-256.
Envelope identity routing, SafeClipboard, W3C text-position selector evidence, cross-engine browser assurance, headless deterministic Markdown serialization, CSS paged-media output, accessible placeholder semantics, DOCX informative PNG figures, bounded rich-text runs, bounded paragraph alignment, bounded heading alignment, and bounded external hyperlinks are implemented on protected `main`. The bounded single-section DOCX page-layout contract remains Proposed / `implemented_on_active_pr` in PR #141 and must not be promoted to protected behavior until integration. The W3C selector remains revision-scoped and projection-version-scoped and must satisfy `0 <= start <= end <= projectedCodePointLength`; protected integration does not transfer annotation persistence, source identity, authorization, or re-anchoring authority from the host. The browser gate being protected does not let a future release reuse historical browser evidence: the exact release candidate must generate fresh evidence bound to its own source, committed synthetic corpus, package-lock SHA-256, run identity, browser revisions, and packed npm artifact SHA-256.

Documentation must not promote Proposed or Planned capabilities to Implemented merely because a PR, issue, or design document is detailed.

Expand Down
80 changes: 80 additions & 0 deletions docs/adr/0027-bounded-docx-page-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# ADR 0027: Bounded single-section DOCX page layout

Status: Proposed

Implementation maturity: `implemented_on_active_pr` in PR #141; not protected-main behavior until that PR is integrated.

## Context

Inkspan Office already owns deterministic, network-free JSON-to-DOCX rendering for a deliberately bounded set of document constructs. Protected `main` does not expose page size, orientation, or page margins, so Word documents inherit the renderer template defaults. Enterprise reports and handoffs often need an explicit physical page contract, but exposing arbitrary WordprocessingML or unconstrained section authoring would substantially enlarge the fidelity, compatibility, and security surface.

`python-docx` 1.2.0 models page dimensions, orientation, and margins on a Word `Section`. Its current section guidance explicitly describes a section as the page-layout authority and shows that landscape orientation requires the page width and height to be swapped together with the orientation property. ECMA-376 identifies Office Open XML as the normative vocabulary and document representation for DOCX packages.

## Alternatives considered

1. **Keep implicit template defaults.** Lowest implementation risk, but leaves buyer-visible physical layout nondeterministic from the request contract and cannot express common A4/Letter requirements.
2. **Expose arbitrary dimensions and raw section properties.** Maximizes flexibility but creates a broad OOXML/layout authority, increases invalid or nonsensical combinations, and makes deterministic support claims harder to prove.
3. **Add multi-section authoring now.** Supports mixed layouts but creates ordering, section-break, header/footer, and inheritance semantics that are not required for the immediate buyer gap.
4. **Selected: one optional complete bounded `page_layout` object for the existing single section.** Gives deterministic common page setup while keeping the contract small and fail-closed.

## Decision

For DOCX requests only, Inkspan may accept an optional top-level `page_layout` object with this exact complete shape:

- `paper_size`: `a4` or `letter`;
- `orientation`: `portrait` or `landscape`;
- `margins_mm`: required integer `top`, `right`, `bottom`, and `left`, each from 0 through 100.

A4 maps to 210 × 297 mm. Letter maps to 8.5 × 11 inches. Landscape sets the Word orientation and swaps the physical width and height. The layout applies only to the document's existing single section. Missing fields, unknown fields, aliases, case variants, booleans, floats, strings, nulls, out-of-range margins, and multiple sections fail closed. When `page_layout` is absent, the existing renderer behavior is preserved.

The public safety facade strips `page_layout` before invoking the established content renderer, applies the validated section settings to the returned DOCX, and then performs the existing deterministic OOXML canonicalization. This preserves the internal content-renderer payload contract while keeping page-layout validation at the public product boundary.

## Consequences and ownership trade-offs

The selected contract covers the common A4/Letter portrait/landscape use case without granting arbitrary page geometry, multi-section layout, headers, footers, gutters, columns, mirrored margins, page numbering, printer selection, CSS-to-Word mapping, or PDF-service authority. Hosts still own source-format interpretation, export authorization, storage, distribution, and business-specific layout policy.

Reopening and saving the generated package through `python-docx` is an intentional implementation step. Compatibility is therefore asserted only for the tested Inkspan-generated single-section document surface, not for arbitrary third-party DOCX round trips.

## Failure and recovery semantics

Invalid layout input raises the existing bounded `OfficeDocumentError` path and never publishes a partial output. `write_office_document()` renders completely before atomic publication, so layout rejection cannot leave a destination file containing a partially modified package. A multiple-section package is rejected instead of guessing which sections should inherit the request.

Recovery is caller-owned: correct the request and rerun rendering. Inkspan does not mutate a durable source document, retry against a remote service, or retain a failed package.

## Security and privacy impact

The contract adds no network access, filesystem input authority, macros, raw OOXML, model call, credential, identity, tenant, persistence, audit, or print-service authority. Numeric and enum bounds prevent arbitrary section XML from entering the renderer. Existing XML/resource validation, deterministic packaging, redacted error, and atomic-publication boundaries remain in force.

The request may still contain document content, so hosts remain responsible for authorization, privacy classification, retention, logging, and storage. Page-layout values themselves are ordinary document-format metadata and are not authorization or provenance evidence.

## Compatibility and migration

The field is optional. Existing DOCX requests without `page_layout` keep the prior contract and output behavior. XLSX and PPTX request schemas are unchanged. A future expansion to arbitrary paper dimensions or multiple sections requires a new ADR or an explicit superseding revision rather than silently widening this decision.

Because the feature is still on an active Draft PR, downstream hosts must not depend on it as a released capability. Version/CHANGELOG promotion occurs only after the stable 0.6.0 release boundary is complete and this feature is accepted for the next release line.

## Verification and acceptance

Acceptance requires all of the following on one exact live-base-integrated head:

- schema evidence for the exact object shape and bounds;
- real `python-docx` round trips proving A4 landscape and Letter portrait physical dimensions, orientation, and margins;
- fail-closed missing/type/enum/range/unknown-field tests;
- rejection of multiple sections;
- deterministic repeated-render evidence;
- no-partial-publication evidence;
- Python 3.11–3.14 Office verification with 100% owned production statement/branch and public-docstring coverage;
- repository CI, security scanning, package verification, and applicable review evidence;
- canonical buyer documentation, doctoring, traceability, and next-release metadata reconciled before Ready/merge evaluation.

PR #141 currently supplies the implementation and exact-head machine evidence but remains Draft because issue #118 still owns the 0.6.0 registry operational boundary. This ADR therefore remains Proposed.

## Rollback or supersession

Before protected integration, rollback is branch closure. After a future accepted merge, rollback is a normal revert that removes the optional schema field, page-layout adapter, tests, and associated release documentation together. A revert must not leave documentation claiming support that protected code no longer provides. Any later widening to multiple sections or arbitrary dimensions should supersede this ADR explicitly.

## Standards and implementation basis

- Ecma International. (2021). *ECMA-376: Office Open XML file formats* (5th ed.). https://ecma-international.org/publications-and-standards/standards/ecma-376/
- python-docx contributors. (2025). *Working with sections (python-docx 1.2.0 documentation).* https://python-docx.readthedocs.io/en/latest/user/sections.html
- python-docx contributors. (2025). *Section objects (python-docx 1.2.0 documentation).* https://python-docx.readthedocs.io/en/stable/api/section.html
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ This index records durable architectural decisions. Protected-main implementatio
| [0024](0024-bounded-docx-paragraph-alignment.md) | Accepted | Bounded paragraph alignment in deterministic DOCX output |
| [0025](0025-bounded-docx-heading-alignment.md) | Accepted | Bounded heading alignment in deterministic DOCX output |
| [0026](0026-bounded-docx-external-hyperlinks.md) | Accepted | Bounded external hyperlinks in deterministic DOCX rich text |
| [0027](0027-bounded-docx-page-layout.md) | Proposed | Bounded single-section page layout in deterministic DOCX output |

## Decision discipline

Expand Down
41 changes: 41 additions & 0 deletions docs/doctoring/docx-page-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# DOCX page-layout doctoring

Status: `implemented_on_active_pr` (PR #141); not protected-main implementation authority.

## Claim under review

Inkspan's proposed DOCX `page_layout` contract makes common physical page setup deterministic without expanding the Office renderer into arbitrary WordprocessingML or a print/PDF service. The bounded contract is intentionally limited to A4 or Letter, portrait or landscape, and explicit integer edge margins from 0 through 100 mm on the renderer's existing single section.

## Primary-source basis

The current `python-docx` 1.2.0 section documentation states that Word sections own page-layout settings such as margins and orientation. Its documented landscape example changes the orientation and swaps `page_width` and `page_height`; the stable Section API exposes orientation, page width/height, and edge margins as read/write properties. Inkspan follows that public API rather than writing raw `w:sectPr` XML.

ECMA-376 is the normative Office Open XML standard family for document representation and packaging. The current Ecma publication page identifies ECMA-376 as *Office Open XML file formats* and the 5th edition as the current listed edition. Inkspan does not claim to implement the entire WordprocessingML layout vocabulary; the standard is the interoperability basis for the generated package while `python-docx` is the concrete library boundary exercised in tests.

## Why the contract is narrow

A fully general Word page-layout surface would require policy for arbitrary dimensions, section breaks, headers/footers, gutters, columns, mirrored margins, inheritance, and mixed orientation. Those are independent fidelity commitments and would make invalid combinations much harder to reject deterministically. The selected contract instead covers two common paper sizes and one complete section-level layout object. Unknown or partial shapes fail closed rather than inheriting hidden defaults.

## Fidelity and determinism evidence

PR #141's permanent tests reopen generated DOCX bytes with `python-docx` and check the actual section properties for A4 landscape and Letter portrait. A4 uses 210 × 297 mm; Letter uses 8.5 × 11 inches. Landscape swaps width/height and sets `WD_ORIENT.LANDSCAPE`. All four margins are checked after round trip.

The feature is applied before Inkspan's existing OOXML canonicalization step. Repeated rendering of the same request is required to remain byte-identical. A multiple-section package fails closed so the implementation never guesses which section should receive the caller's layout.

## Safety, privacy, and operational boundary

The field cannot introduce network fetches, file reads, macros, raw OOXML, model execution, credentials, tenant context, durable persistence, or printer/PDF-service authority. Existing XML/resource validation and atomic output publication remain authoritative. Invalid layout fails before the destination file is published.

Hosts still decide whether an author may export, which page layout is appropriate for a business document, where the artifact may be stored, how it is distributed, and whether its content requires special privacy controls. Layout metadata is not authentication, authorization, provenance, or audit evidence.

## Compatibility and rollback

Omitting `page_layout` preserves the pre-existing DOCX request path. XLSX and PPTX schemas remain unchanged. Until PR #141 is merged, all documentation must classify the feature as active-PR behavior. Rollback before merge is branch closure; after a future merge, code, schema, tests, ADR status, buyer documentation, traceability, and release notes must be reverted together.

## APA 7 references

Ecma International. (2021). *ECMA-376: Office Open XML file formats* (5th ed.). https://ecma-international.org/publications-and-standards/standards/ecma-376/

python-docx contributors. (2025). *Section objects (python-docx 1.2.0 documentation).* https://python-docx.readthedocs.io/en/stable/api/section.html

python-docx contributors. (2025). *Working with sections (python-docx 1.2.0 documentation).* https://python-docx.readthedocs.io/en/latest/user/sections.html
Loading
Loading