Both formats are read-only. A file opens as a Document or a Workbook by conversion: the reader turns the 2003 file into an in-memory
OOXML package (DOCX or XLSX) on the markup layer and the hub wraps that package like any other. What the hub then holds saves as DOCX or XLSX,
extracts as text and converts as any OOXML file does. Document.ImportLoss and Workbook.ImportLoss list what did not map (null for a file
that was opened natively). Writing the 2003 formats is not supported.
using Document doc = Document.Open("old.xml"); // sniffed: Word 2003 XML using Workbook wb = Workbook.Open("old.xml"); // sniffed: Excel 2003 XML Office.Convert("old.xml", "new.docx"); // or .xlsx LossReport? lost = doc.ImportLoss;
The static entry points are WordMl2003.ToPackage/Open (RecursiveAI.OneDocument.Documents.Xml2003) and SpreadsheetMl2003.ToPackage/Open
(RecursiveAI.OneDocument.Spreadsheets.Xml2003); they take an IByteSource, an OpenContext and an optional ILossSink.
Identification
By content, never the extension (FormatSniffer): the mso-application processing instruction (Word.Document, Excel.Sheet) or the root
namespace (http://schemas.microsoft.com/office/word/2003/wordml, urn:schemas-microsoft-com:office:spreadsheet), ahead of generic XML. The
files are .xml (Word's "Word 2003 XML Document", Excel's "XML Spreadsheet 2003") and sometimes .doc or .xls by name.
Reading rules
- The file is parsed once with the markup tokenizer (no
System.Xml). DTDs and entity declarations are refused; nesting, attribute counts and part size obeyOfficeLimits. Not well-formed XML isxml.*; a root that is not the format's iswml2003.not-wordmlorsml2003.not-spreadsheet. Everything else is repaired and reported (defects.md); the reader raises nothing butOfficeException. - Order in the output follows the ECMA-376 schema sequences, not the order in the 2003 file: property lists (paragraph, run, table, row, cell, section, style, list level) are read into a set and written in schema order. A property that appears twice keeps its last occurrence.
- Hyperlink targets become external relationships. Nothing is fetched, linked pictures are not followed, and data in
w:binDatais decoded but never executed. - Names carry hyphens in 2003 (
w:first-line,w:h-ansi,w:line-rule) and camel case in 2006. Attribute and element names convert by that rule, withw:fareast→eastAsia. Hyphenated enumeration values (dash-long,next-page,at-least) convert the same way. Fractional measures (LibreOffice writes4320.5401) round to whole numbers; empty attributes are left out.
Word
| 2003 | Becomes |
|---|---|
o:DocumentProperties | docProps/core.xml (title, subject, author, keywords, description, last author, revision, created, saved, category) and docProps/app.xml (company, manager, pages, words, characters, lines, paragraphs, characters with spaces, total time) |
o:CustomDocumentProperties | docProps/custom.xml (dt:dt string, boolean, integer, float) |
w:fonts/w:defaultFonts | w:docDefaults/w:rPrDefault/w:rFonts |
w:styles/w:style | word/styles.xml; wx:uiName is a hint and ignored (w:name is kept); latent styles are dropped |
w:lists (w:listDef, w:list) | word/numbering.xml (w:abstractNum, w:num, level overrides); w:nfc codes map to number formats; w:listPr in a paragraph becomes w:numPr |
w:docPr | word/settings.xml: zoom, track revisions, default tab stop, character spacing control; even-page headers set w:evenAndOddHeaders |
wx:sect, wx:sub-section | read through; a section's w:sectPr goes on the last paragraph before it, the last section's closes the body |
w:hdr, w:ftr (in w:sectPr) | word/headerN.xml, word/footerN.xml with odd → default, even, first |
w:p, w:r, w:t, w:tab, w:br, w:sym, … | the same elements; a run nested in a run (LibreOffice inside a hyperlink) becomes a sibling |
w:tbl | tables with grid, w:hmerge → w:gridSpan, w:vmerge → w:vMerge; a missing grid is made from the widest row; a cell always ends with a paragraph |
w:fldSimple, w:fldChar | unchanged |
w:hlink | w:hyperlink (w:dest → relationship, w:bookmark → w:anchor) |
aml:annotation bookmark start/end | w:bookmarkStart/End |
aml:annotation Word.Comment* | word/comments.xml, w:commentRangeStart/End, w:commentReference |
aml:annotation Word.Insertion, Word.Deletion | w:ins, w:del (text becomes w:delText) with author and date |
w:footnote, w:endnote (inline in a run) | word/footnotes.xml, word/endnotes.xml with separators, and the reference |
w:pict with v:imagedata and w:binData | a DrawingML inline picture (word/media/imageN.*; the type is taken from the bytes, the size from the VML style) |
w:pict with v:textbox | a VML text box with converted content |
w:smartTag, w:customXml, w:sdt | removed, text kept |
Dropped, with loss.* codes: loss.unmapped-markup.<element> (any element without a counterpart, one line per element name),
loss.shapes (VML shapes with neither picture nor text), loss.pictures, loss.ole-objects (reduced to the preview picture),
loss.smart-tags, loss.custom-xml, loss.formatting-revisions (tracked formatting changes), loss.comments (a comment anchored between
paragraphs). Proofing marks and permission ranges are dropped silently: they describe the editing session, not the document.
Excel
| 2003 | Becomes |
|---|---|
Worksheet, Table, Row, Cell | sheets, rows, cells. ss:Index places an item (a backward index follows the previous item, sml2003.bad-index); ss:Span repeats a column or row; ss:MergeAcross/MergeDown merge |
Data ss:Type | Number; String (inline string); DateTime (ISO 8601 → serial in the workbook's date system; a time alone on 1899-12-31 is a fraction of a day); Boolean; Error |
ss:Data with HTML (B, I, U, S, Sub, Sup, Font) | rich inline string runs |
ss:Formula (R1C1) | A1 text through the public formula parser and printer; the cached Data stays as the cached result; nothing is recalculated on open. ss:ArrayRange gives an array formula. Text the parser rejects is sml2003.bad-formula and the value stays as a constant |
Styles/Style | cell formats; ss:Parent is resolved (a style with no parent inherits from Default); the number format names of the 2003 documentation (Short Date, Percent, Currency …) map to format codes |
Column, Row attributes | widths (points → characters, below), heights, ss:Hidden, ss:StyleID (a cell without a style takes its row's, then its column's) |
Names, NamedRange, NamedCell | defined names (workbook and sheet level); Print_Area, Print_Titles, Criteria, Extract, _FilterDatabase become the _xlnm. names |
Comment | xl/commentsN.xml and a legacy drawing so the note shows |
ss:HRef | a hyperlink (#Sheet!A1 is internal) |
x:ConditionalFormatting | cellIs and expression rules with a differential format from the CSS in Format Style |
x:DataValidation | whole, decimal, list, date, time, text length and custom rules |
x:AutoFilter | an auto filter range |
x:WorksheetOptions | tab selection, gridlines and headings, right-to-left, zoom, frozen panes, protection, page margins, orientation, scale, paper size, centering, header and footer |
x:ExcelWorkbook | date system (Date1904), active sheet, structure protection |
o:DocumentProperties | core and app properties |
Deviations and choices:
- Column widths. 2003 stores points; XLSX stores characters of the default font's digit. The reader assumes a 7 pixel digit and 5 pixels of
padding at 96 dpi:
(points × 96 / 72 − 5) / 7, rounded to two places (48 points is 8.43 characters). - Duplicate style IDs. The first definition wins (
sml2003.duplicate-style). LibreOffice writesDefaulttwice, the second time in full. - Named styles. A named cell style (
ss:Nameother thanDefault) becomes a plain cell format; the name is reported asloss.cell-style-names. - Sheet names. Characters
[]:*?/\are removed, names are cut to 31 characters and made unique ignoring case (sml2003.sheet-name). - OpenFormula text. LibreOffice's "Microsoft Excel 2003 XML" export writes
of:=[.B2]+[.B3]inss:Formula. That is not R1C1 and is not read here (OpenFormula belongs to the ODF reader); each such formula is a defect and its cached value is kept. - Not carried over: manual page breaks (
loss.page-breaks), sorting, pane details beyond the frozen split, printer settings.
Checks and open questions
Checked on the build host: every file the sniffer calls Word or Excel 2003 XML among the committed fixtures and 87 Word and 4 Excel files written by
LibreOffice from its own corpus opens with typed defects only; their DOCX/XLSX conversions validate against the pinned ECMA-376 Transitional schemas
(1,157 of 1,158 parts; the one miss is xml:space on a spreadsheet t element, which the schema does not declare and Excel itself writes).
Unanswered by Office (to be answered on the oracle machine):
- Does Word open the DOCX converted from
testdata/xml2003/word-features.xmlandlo-word.xmlwithout a repair prompt, and does Word's own conversion of the 2003 file match (paragraph count, numbering labels, revisions, comment text, picture size)? - Does Excel open the XLSX converted from
excel-features.xmlwithout repair (comments with the legacy drawing, conditional formats, validation), and does Excel's own "Save as .xlsx" of the 2003 file give the same values, formats and formulas? - Which style wins when a style ID is defined twice, and does a cell without
ss:StyleIDtake its row's or its column's style? - What column width does Excel give
ss:Width="64.008"on a machine with the default font (the width formula above is an estimate)?
Regenerate the conversions with tools/Xml2003Probe convert <dir> <out>; tools/xml2003/ holds the inputs LibreOffice converted for the fixtures.