Skip to main content

OOXML ↔ SFDT Element Mapping

Overview​

This guide shows exactly how OOXML structures transform to Syncfusion's SFDT format and back. Understanding these mappings is critical for working with both formats in the AppraiSys system.


Core Concept Differences​

OOXML (Office Open XML)​

  • Format: XML files in ZIP archive
  • Structure: Hierarchical XML elements
  • Namespaces: Multiple (w:, r:, a:, etc.)
  • Relationships: Separate .rels files
  • Images: Stored in /word/media/

SFDT (Syncfusion Document Format)​

  • Format: Single JSON object
  • Structure: Nested JSON objects and arrays
  • No namespaces: Just property names
  • Relationships: Embedded in JSON
  • Images: Base64 encoded or URLs inline

Document Structure Mapping​

OOXML Document Root​

<w:document xmlns:w="...">
<w:body>
<w:p>...</w:p>
<w:tbl>...</w:tbl>
</w:body>
</w:document>

SFDT Document Root​

{
"sections": [
{
"blocks": [
{ "paragraphFormat": {...}, "inlines": [...] },
{ "table": {...} }
],
"sectionFormat": {...}
}
]
}

Section Mapping​

OOXML Section​

Sections defined by w:sectPr elements:

<w:body>
<w:p>...</w:p>
<w:p>...</w:p>

<!-- Section break -->
<w:p>
<w:pPr>
<w:sectPr>
<w:pgSz w:w="12240" w:h="15840"/> <!-- Letter size -->
<w:pgMar w:top="1440" w:right="1440"
w:bottom="1440" w:left="1440"/>
<w:cols w:space="720"/>
</w:sectPr>
</w:pPr>
</w:p>

<w:p>...</w:p> <!-- New section -->
</w:body>

SFDT Section​

{
"sections": [
{
"sectionFormat": {
"pageWidth": 612, // 8.5 inches in points
"pageHeight": 792, // 11 inches in points
"leftMargin": 72, // 1 inch in points
"rightMargin": 72,
"topMargin": 72,
"bottomMargin": 72,
"differentFirstPage": false,
"differentOddAndEvenPages": false,
"headerDistance": 36,
"footerDistance": 36
},
"blocks": [...]
}
]
}

Key differences:

  • OOXML: Twips (1440 per inch)
  • SFDT: Points (72 per inch)
  • OOXML: Width/height in page size
  • SFDT: Width/height directly

Conversion: points = twips / 20


Paragraph Mapping​

OOXML Paragraph​

<w:p>
<w:pPr>
<w:pStyle w:val="Normal"/>
<w:jc w:val="center"/>
<w:spacing w:before="120" w:after="120"
w:line="276" w:lineRule="auto"/>
<w:ind w:left="720" w:right="0" w:firstLine="360"/>
</w:pPr>

<w:r>
<w:rPr>
<w:b/>
<w:sz w:val="28"/>
</w:rPr>
<w:t>Hello World</w:t>
</w:r>
</w:p>

SFDT Paragraph (Block)​

{
"paragraphFormat": {
"styleName": "Normal",
"textAlignment": "Center",
"beforeSpacing": 6, // 120 twips / 20 = 6 points
"afterSpacing": 6,
"lineSpacing": 13.8, // 276 / 20 = 13.8 points
"lineSpacingType": "Multiple",
"leftIndent": 36, // 720 / 20 = 36 points
"rightIndent": 0,
"firstLineIndent": 18 // 360 / 20 = 18 points
},
"inlines": [
{
"text": "Hello World",
"characterFormat": {
"bold": true,
"fontSize": 14 // 28 half-points / 2 = 14 points
}
}
]
}

Key mapping:

  • w:p → block object with paragraphFormat
  • w:pPr → paragraphFormat properties
  • w:r → inline object in inlines array
  • w:t → text property
  • w:rPr → characterFormat properties

Run (Inline) Mapping​

OOXML Run​

<w:r>
<w:rPr>
<w:rFonts w:ascii="Times New Roman"/>
<w:sz w:val="24"/> <!-- 12pt -->
<w:color w:val="FF0000"/> <!-- Red -->
<w:b/>
<w:i/>
<w:u w:val="single"/>
</w:rPr>
<w:t xml:space="preserve">Text content</w:t>
</w:r>

SFDT Inline​

{
"text": "Text content",
"characterFormat": {
"fontFamily": "Times New Roman",
"fontSize": 12, // 24 half-points / 2
"fontColor": "#FF0000FF", // RGBA format
"bold": true,
"italic": true,
"underline": "Single"
}
}

Property mappings:

OOXMLSFDTNotes
w:rFonts/@w:asciifontFamilyFont name
w:sz/@w:valfontSizeDivide by 2 (half-points → points)
w:color/@w:valfontColorAdd alpha channel (#RRGGBBAA)
w:bbold: trueBoolean
w:iitalic: trueBoolean
w:u/@w:valunderlineEnum value

Table Mapping​

OOXML Table​

<w:tbl>
<w:tblPr>
<w:tblW w:w="5000" w:type="pct"/>
<w:tblBorders>
<w:top w:val="single" w:sz="4"/>
<w:left w:val="single" w:sz="4"/>
<w:bottom w:val="single" w:sz="4"/>
<w:right w:val="single" w:sz="4"/>
</w:tblBorders>
</w:tblPr>

<w:tblGrid>
<w:gridCol w:w="3000"/>
<w:gridCol w:w="3000"/>
</w:tblGrid>

<w:tr>
<w:tc>
<w:tcPr>
<w:tcW w:w="3000" w:type="dxa"/>
</w:tcPr>
<w:p><w:r><w:t>Cell 1</w:t></w:r></w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="3000" w:type="dxa"/>
</w:tcPr>
<w:p><w:r><w:t>Cell 2</w:t></w:r></w:p>
</w:tc>
</w:tr>
</w:tbl>

SFDT Table​

{
"table": {
"rows": [
{
"cells": [
{
"cellFormat": {
"cellWidth": 150, // 3000 / 20 = 150 points
"preferredWidth": 150,
"preferredWidthType": "Point"
},
"blocks": [
{
"inlines": [
{ "text": "Cell 1" }
]
}
]
},
{
"cellFormat": {
"cellWidth": 150,
"preferredWidth": 150,
"preferredWidthType": "Point"
},
"blocks": [
{
"inlines": [
{ "text": "Cell 2" }
]
}
]
}
]
}
],
"tableFormat": {
"preferredWidth": 100,
"preferredWidthType": "Percent",
"borders": {
"top": { "lineStyle": "Single", "lineWidth": 0.5 },
"left": { "lineStyle": "Single", "lineWidth": 0.5 },
"bottom": { "lineStyle": "Single", "lineWidth": 0.5 },
"right": { "lineStyle": "Single", "lineWidth": 0.5 }
}
}
}
}

Key differences:

  • OOXML: Grid definition separate from cells
  • SFDT: No explicit grid, cells define their own width
  • OOXML: Cell contains paragraphs
  • SFDT: Cell contains blocks array (paragraphs)

Image Mapping​

OOXML Image​

<!-- In document.xml -->
<w:p>
<w:r>
<w:drawing>
<wp:inline>
<wp:extent cx="914400" cy="914400"/> <!-- 1 inch × 1 inch in EMUs -->
<a:graphic>
<a:graphicData>
<pic:pic>
<pic:blipFill>
<a:blip r:embed="rId5"/> <!-- Relationship reference -->
</pic:blipFill>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
</w:p>

<!-- In document.xml.rels -->
<Relationship Id="rId5"
Type="...relationships/image"
Target="media/image1.png"/>

<!-- Actual file -->
<!-- /word/media/image1.png (binary) -->

SFDT Image​

{
"inlines": [
{
"imageString": "data:image/png;base64,iVBORw0KGgoAAAANSUhEU...",
"width": 72, // 1 inch in points
"height": 72,
"name": "image1.png",
"alternateText": "",
"isInlineImage": true
}
]
}

Or with URL:

{
"inlines": [
{
"imageString": "https://example.com/image.png",
"width": 72,
"height": 72,
"isInlineImage": true
}
]
}

Conversion process:

Unit conversion:

  • OOXML: EMUs (914,400 per inch)
  • SFDT: Points (72 per inch)
  • Formula: points = emus / 12700

From AppraiSys (DocumentToPdfConverter.cs):

// Extract images from SFDT
var sfdtWrapper = JsonSerializer.Deserialize<SfdtWrapper>(sfdt);
var imagesToDownload = sfdtWrapper?.Images
.Where(x => x.Value.Count == 1 &&
(x.Value.FirstOrDefault()?.StartsWith("http") ?? false))
.ToList();

// Download and convert to base64
foreach (var downloadedImage in downloadedImages)
{
sfdtWrapper.Images[downloadedImage.Success.BaseData.Key].Insert(0,
$"data:{contentType};base64,{Convert.ToBase64String(bytes)}"
);
}

<!-- In document.xml -->
<w:hyperlink r:id="rId7">
<w:r>
<w:rPr>
<w:color w:val="0000FF"/>
<w:u w:val="single"/>
</w:rPr>
<w:t>Click here</w:t>
</w:r>
</w:hyperlink>

<!-- In document.xml.rels -->
<Relationship Id="rId7"
Type="...relationships/hyperlink"
Target="https://example.com"
TargetMode="External"/>
{
"inlines": [
{
"fieldType": 0,
"hasFieldEnd": true,
"formFieldData": null
},
{
"text": "HYPERLINK \"https://example.com\""
},
{
"fieldType": 2
},
{
"text": "Click here",
"characterFormat": {
"fontColor": "#0000FFFF",
"underline": "Single"
}
},
{
"fieldType": 1
}
]
}

SFDT uses field codes for hyperlinks (similar to MERGEFIELD approach)


Style Mapping​

OOXML Style Definition​

<!-- In styles.xml -->
<w:style w:type="paragraph" w:styleId="Heading1">
<w:name w:val="Heading 1"/>
<w:basedOn w:val="Normal"/>
<w:next w:val="Normal"/>
<w:qFormat/>
<w:pPr>
<w:spacing w:before="240" w:after="120"/>
<w:jc w:val="center"/>
</w:pPr>
<w:rPr>
<w:b/>
<w:sz w:val="32"/> <!-- 16pt -->
</w:rPr>
</w:style>

SFDT Style​

{
"name": "Heading 1",
"type": "Paragraph",
"paragraphFormat": {
"beforeSpacing": 12, // 240 / 20
"afterSpacing": 6, // 120 / 20
"textAlignment": "Center"
},
"characterFormat": {
"bold": true,
"fontSize": 16 // 32 / 2
},
"basedOn": "Normal",
"next": "Normal"
}

Styles location:

  • OOXML: /word/styles.xml
  • SFDT: styles array in root object

Content Control (SDT) Mapping​

OOXML SDT​

<w:sdt>
<w:sdtPr>
<w:id w:val="123456"/>
<w:tag w:val="PropertyAddress"/>
<w:alias w:val="Property Address"/>
<w:lock w:val="sdtContentLocked"/>
</w:sdtPr>
<w:sdtContent>
<w:p>
<w:r><w:t>123 Main Street</w:t></w:r>
</w:p>
</w:sdtContent>
</w:sdt>

SFDT Content Control​

{
"contentControlProperties": {
"lockContentControl": true,
"lockContents": false,
"tag": "PropertyAddress",
"title": "Property Address",
"appearance": "BoundingBox",
"type": "RichText",
"hasPlaceHolderText": false,
"multiline": false,
"isTemporary": false,
"characterFormat": {},
"contentControlListItems": []
},
"blocks": [
{
"inlines": [
{ "text": "123 Main Street" }
]
}
]
}

Property mappings:

OOXMLSFDT
w:id(not directly mapped)
w:tagtag
w:aliastitle
w:lock="sdtContentLocked"lockContentControl: true, lockContents: false

Complete Transformation Example​

OOXML Document​

<w:document>
<w:body>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:sz w:val="28"/>
</w:rPr>
<w:t>Title</w:t>
</w:r>
</w:p>

<w:p>
<w:r><w:t>Normal text.</w:t></w:r>
</w:p>

<w:tbl>
<w:tblGrid>
<w:gridCol w:w="3000"/>
<w:gridCol w:w="3000"/>
</w:tblGrid>
<w:tr>
<w:tc>
<w:p><w:r><w:t>A1</w:t></w:r></w:p>
</w:tc>
<w:tc>
<w:p><w:r><w:t>B1</w:t></w:r></w:p>
</w:tc>
</w:tr>
</w:tbl>
</w:body>
</w:document>

SFDT Document​

{
"sections": [
{
"blocks": [
{
"paragraphFormat": {
"textAlignment": "Center"
},
"inlines": [
{
"text": "Title",
"characterFormat": {
"bold": true,
"fontSize": 14
}
}
]
},
{
"inlines": [
{ "text": "Normal text." }
]
},
{
"table": {
"rows": [
{
"cells": [
{
"blocks": [
{
"inlines": [
{ "text": "A1" }
]
}
]
},
{
"blocks": [
{
"inlines": [
{ "text": "B1" }
]
}
]
}
]
}
]
}
}
],
"sectionFormat": {}
}
]
}

Unit Conversion Reference​

Length Units​

UnitOOXMLSFDTConversion
Font sizeHalf-points (w:sz)PointsDivide by 2
SpacingTwips (1/20 pt)PointsDivide by 20
Width/HeightTwipsPointsDivide by 20
Image sizeEMUs (914400/inch)Points (72/inch)Divide by 12700

Examples​

OOXML → SFDT
w:sz="24" → fontSize: 12
w:spacing before="240" → beforeSpacing: 12
w:tcW w="3000" → cellWidth: 150
wp:extent cx="914400" → width: 72

Common Transformation Issues​

Issue 1: Lost Formatting​

Problem: Bold/italic lost in conversion

Cause: Run properties not preserved

Solution: Ensure characterFormat includes all properties

Issue 2: Image Size Wrong​

Problem: Images appear too large or small

Cause: Unit conversion error

Solution: Use correct formula (EMUs / 12700 = points)

Issue 3: Table Layout Broken​

Problem: Cells wrong width after conversion

Cause: Grid definition vs cell widths mismatch

Solution: Recalculate cell widths based on table format

Issue 4: Extra Paragraphs​

Problem: Blank paragraphs appear

Cause: SFDT merge artifacts

Solution: Clean empty blocks after conversion

From AppraiSys: Known Syncfusion limitation documented in patches


AppraiSys Conversion Patterns​

DOCX to SFDT​

// From DocumentToSfdtFormat.cs
public static string Convert(MemoryStream stream)
{
var doc = new Syncfusion.DocIO.DLS.WordDocument(
stream,
Syncfusion.DocIO.FormatType.Docx
);

var sfdtDocument = Syncfusion.EJ2.DocumentEditor.WordDocument.Load(doc);
sfdtDocument.OptimizeSfdt = true; // Optimize output

var sfdt = Newtonsoft.Json.JsonConvert.SerializeObject(sfdtDocument);

sfdtDocument.Dispose();
doc.Dispose();

return sfdt;
}

SFDT to DOCX​

// From DocumentToPdfConverter.cs
var sfdtDocument = Syncfusion.EJ2.DocumentEditor.WordDocument.Save(
mergedSfdt,
Syncfusion.EJ2.DocumentEditor.FormatType.Docx
);

using var wordDocument = new WordDocument(
sfdtDocument,
Syncfusion.DocIO.FormatType.Docx
);

Debugging Transformations​

Compare Structures​

// 1. Start with DOCX
var docx = WordprocessingDocument.Open("original.docx", false);

// 2. Convert to SFDT
var sfdt = DocumentToSfdtFormat.Convert(docxStream);

// 3. Convert back to DOCX
var sfdtStream = WordDocument.Save(sfdt, FormatType.Docx);
var docx2 = WordprocessingDocument.Open(sfdtStream, false);

// 4. Compare
CompareDocuments(docx, docx2);

Use Syncfusion Document Editor​

// In browser with Syncfusion Document Editor
const documentEditor = new DocumentEditor();
documentEditor.open(sfdtJson);

// Inspect structure
console.log(documentEditor.documentEditor.document);

Quick Reference​

Structure Mappings​

OOXMLSFDT
w:document → w:bodysections array
w:pblock with inlines
w:r + w:tinline with text
w:tbltable object
w:trrow in rows array
w:tccell in cells array
w:pPrparagraphFormat
w:rPrcharacterFormat
w:drawinginline with imageString

Next Steps​

➡️ Next: Diagnostics & Debugging

Related:


💡 Remember: OOXML is explicit and verbose, SFDT is compact and JSON-based. Both represent the same document—just different formats!