Skip to content

Latest commit

 

History

History
186 lines (156 loc) · 11.5 KB

File metadata and controls

186 lines (156 loc) · 11.5 KB

Compatibility matrix

What survives a transplant from A into B, honestly. "Preserved verbatim" means the XML is deep-copied unchanged; "remapped" means references were rewritten so the element still resolves in the new package.

Legend: ✅ full · 🟡 partial · ⚠️ preserved but unverified · ❌ not carried across (always reported, never silent)

File formats

Format Read Write Notes
.docx Primary format
.docm Macro part copied byte for byte; not executed, not scanned
.dotx / .dotm Handled as .docx; output is .docx
.doc (97-2003) 🟡 Read-only, text only — see ADR 0002
.rtf Detected and refused with an explanation
Flat OPC .xml Detected and refused with an explanation
Encrypted / password protected .doc Detected via the FIB flag and refused; no decryption attempted
Encrypted / password protected .docx Refused, but as a generic parse error — the OLE container is routed to the .doc reader, which reports a missing WordDocument stream rather than "encrypted"

Content elements (.docx source)

Element Status Detail
Paragraphs and runs Deep-copied with all properties
Bold / italic / underline / strike Run properties preserved verbatim
Font, size, colour, highlight Preserved verbatim
Alignment, spacing, line spacing Preserved verbatim
Indentation 🟡 Left/right indents are rebased onto B's page margins when A's section break is dropped, so the content keeps the inset it was authored with instead of shifting sideways. First-line and hanging indents are preserved verbatim. See below.
Paragraph and character styles Imported into B; on an id clash with a different definition, imported under a new id so B's own styles are untouched. An imported style arriving with w:default="1" has the flag stripped so B's own default stays authoritative (reported).
Numbered and bulleted lists w:num and w:abstractNum imported, numId remapped, fresh nsid
Multi-level lists Full abstract definition imported
Tables Copied whole, including tblPr, borders, grid and style
Merged cells (gridSpan, vMerge) Preserved on copy; see below for merging two tables
Multi-row headers (tblHeader) Detected and preserved
Nested tables Copied; excluded from cell-level merging
Images (w:drawing) Media part imported, r:embed remapped, deduplicated by content hash
Legacy images (w:pict / VML) r:id remapped like any other relationship
Image captions Ordinary paragraphs; copied with the section
Internal hyperlinks Anchors preserved
External hyperlinks Relationship re-created with TargetMode="External"
Bookmarks Ids renumbered; names kept, or suffixed on a clash (reported)
Cross-references (REF fields) 🟡 Copied. Rewritten when their bookmark was renamed. A reference whose target stays in A will show "Error! Reference source not found" in Word. Reported as a warning.
Footnotes Note content imported into B's footnotes.xml, ids remapped; the part is created with the required separators if B had none
Endnotes Same as footnotes
Equations (OMML) Copied verbatim
Text boxes ⚠️ Copied verbatim including relationships. Internal layout preserved but not independently validated.
Shapes / SmartArt / mc:AlternateContent ⚠️ Both the modern and fallback branch copied verbatim
Charts 🟡 Chart parts and their own relationships imported. A chart linked to an external workbook loses that link. Reported.
OLE embedded objects 🟡 Object data imported. Editing still requires the originating application. Reported.
Content controls (w:sdt) Copied verbatim; shading applied to the content inside
Tracked changes (w:ins / w:del) Copied as-is, still marked as revisions
Review comments Anchors removed from copied content; the commented text is copied, the comment is not. Reported.
Section breaks inside copied content Removed by default so B keeps control of page layout. Configurable via drop_section_breaks. Reported. When they are dropped, horizontal geometry is rebased — see below.
Page breaks w:br type="page" and pageBreakBefore preserved
Headers and footers n/a Section-level, not section content. B's are untouched; A's are never imported.
Unknown / future elements Deep-copied verbatim rather than dropped

Package parts

Part Behaviour
document.xml Rewritten
numbering.xml Rewritten only if a list was imported; created if absent
styles.xml Rewritten only if a style was imported
footnotes.xml / endnotes.xml Rewritten only if a note was imported; created if absent
word/media/* New files added; existing ones untouched
[Content_Types].xml Extended only if a new content type is needed
settings.xml, theme/, customXml/, fontTable.xml, everything else Copied byte for byte, in the original order, with the original compression

Preservation of unknown parts is asserted by a test that adds a customXml/item1.xml part to a template and checks its bytes come out unchanged.

Horizontal geometry

A paragraph's indent is measured from the page margin, not from the page edge. Copying an indent without the margin that framed it therefore moves the content sideways. Because A's section break is dropped by default (so B keeps control of page layout), B's margins are the ones in force — and converters routinely emit a near-zero w:pgMar together with a large style or paragraph indent, which lands on top of a conventional template margin and pushes everything right.

So when the section break is dropped, the copied content's horizontal geometry is re-measured against B's page margins:

Property Behaviour
w:ind/@w:left and @w:start Rebased onto B's margin, clamped at 0
w:ind/@w:right and @w:end Rebased onto B's margin, clamped at 0
w:tblPr/w:tblInd/@w:w Rebased, dxa units only; other unit types untouched
w:ind/@w:firstLine, @w:hanging Untouched — relative to the paragraph's own edge, so they stay correct once that edge moves
@w:leftChars, @w:rightChars, @w:firstLineChars, @w:hangingChars Untouched — character-relative, not margin-relative
Column widths (w:tblGrid, w:tcW) Never rescaled — see the table below

Styles imported from A get the same treatment; a style is not tied to one section, so it is rebased against the body's page setup.

Applied to any content, this is reported once as an informational transplant.rebased-indents issue.

Table width is reported, not fixed. A copied table whose w:tblGrid total exceeds B's text column width is copied exactly as authored and the overflow is reported as a transplant.wide-tables warning. Column widths are not rescaled to fit: deciding how to redistribute width across columns is a layout judgement, and guessing it would silently change the author's table. Adjust it in Word if it should fit.

Fidelity code Severity Meaning
transplant.rebased-indents info Left/right indents were shifted from A's margins to B's
transplant.demoted-default-styles info An imported style's w:default="1" flag was stripped
transplant.wide-tables warning A copied table is wider than B's text column; copied as authored, not rescaled

Shading of retained content

Element Mechanism Status
Paragraph w:pPr/w:shd with w:fill
Table cell w:tcPr/w:shd with w:fill
Nested table cell Same, applied recursively
Content control body Applied to the blocks inside w:sdtContent
Image / drawing Covered by its paragraph's shading
Bookmarks, proofErr, sectPr, comment range markers Cannot carry a background; left unmarked. The markers themselves survive the copy — only the grey marking is absent. Not currently reported (see below). n/a

Known gap. apply/shading.py::_shade_block returns early for these unshadeable markers without appending to report.unsupported, so no shading.unsupported-element issue is raised for them — only genuinely unknown tags are reported today. This is documentation catching up with the code, not a content loss: the markers survive the transplant, they simply are not marked and their absence from the marking is not announced.

What counts as retained. Every piece of B that survives is marked, including the content of a section you chose to skip — that text is exactly what still has to be written by hand, so it carries the same marker as content kept alongside imported material. An overwritten section keeps nothing and so carries no marker, and content imported from A is never marked.

w:shd is inserted at its schema-mandated position within w:pPr / w:tcPr (asserted against the ECMA-376 sequences in tests). Only w:shd is written — text colour, borders, fonts and other properties are untouched. Where retained content already had a non-default fill, it is replaced with the marker and the count is reported as a warning.

Character highlighting (w:highlight) is never used: it is a different feature with different printing behaviour, and using it as a stand-in for table shading would misrepresent the document.

Table merging (combining A's table with B's)

Copying a table is always full fidelity. Merging two tables cell by cell is narrower, because a correspondence between cells has to exist:

Situation Merging available?
Same column count, matching headers, unique key column ✅ all nine actions
Different column order ✅ columns mapped by header similarity
Different column count ✅ unmapped columns reported; merging offered
Low-confidence key column 🟡 offered, but must be confirmed
No unique key column ❌ merging withheld — replace or keep both
Different merged-cell geometry ❌ merging withheld — replace or keep both
Different header row count ❌ merging withheld — replace or keep both
Nested table on either side ❌ merging withheld — replace or keep both

When merging is withheld the tool states which condition blocked it. Replace, keep-template, keep-both and skip remain available in every case, and none of them loses data.

.doc source specifics

Capability Status
Paragraph text (UTF-16 and CP1252 pieces)
Paragraph boundaries
Heading detection 🟡 manual numbering and line shape only; confidence capped at medium
Table content ❌ blocked — convert to .docx
Images ❌ blocked — convert to .docx
Any formatting (fonts, colours, styles, alignment) ❌ imported as plain paragraphs; warned before running
Footnotes, fields, bookmarks ❌ detected for counting only
Writing .doc ❌ output is always .docx

The .doc reader is validated against generated fixtures produced by this project's own CFB writer. That proves it parses the structure it models; it does not prove coverage of the full variety of real-world .doc files. Check the preview before trusting a .doc result.