A few days ago I encountered a question on Stack Overflow about using tabs to align content in a Word document. The required result was something like this:

I started looking at the Word JS API documentation and realized there is nothing in that object model for inserting tabs or defining tab stops.

The first thing I asked myself was, “Why is the functionality missing?” I soon discovered that Word Online does not support displaying tabulated text. It preseves tab information in a document that contains it, but the user won’t recognize that. Indeed, the functionality for creating headers and footers in Word Online inserts a three-column table with left, centered and right alignment! So the missing functionality is a known issue and unlikely to be changed soon. Since the JS API development team gives priority to functionality supported across all platforms, the question of “Why” was answered.

When something is not in the object model, the only recourse for implementing it is to resort to inserting Word Open XML. How to find the required Word Open XML became the next question…

Almost three years ago, when the Office JS APIs were being introduced, I wrote an article about using Word Open XML in order to extend the JavaScript object model. Since that time, the APIs have been refined and extended… but they still don’t provide the full COM object model palette, as evidenced by this issue with tabs and tab-stops.

The information in that article about how to research the Word Open XML remains valid. We no longer need worry about Coercion types, however, unless the add-in targets Word 2013. The later APIs have an insertOoxml method for Range objects.

In this case, to isolate the necessary Word Open XML I created a document containing a single paragraph, with a left-aligned tabstop. I inspected the code in the Open XML Productivity Tool and narrowed down the Word Open XML needed for defining a tab stop to:

<w:body>
  <w:p>
    <w:pPr>
      <w:tabs>
        <w:tab w:val="left" w:pos ="851"/>
      </w:tabs>
    </w:pPr>
    <w:r>
      <w:t></w:t>
    </w:r>
  </w:p>
</w:body>

This needs to be incorporated into the standard, minimal Word Open XML, as described in the article Create better add-ins for Word with Office Open XML.

The second concern is how to write a tab character to a Word document using JavaScript. In a VB-language we’d use vbTab, or possibly Chr(9). JavaScript uses the same syntax as C#: \t, as part of the string. And, since the result should extend over two lines (paragraphs), how to insert a new paragraph using JavaScript is also relevant: \n.

That provides all the ingredients. The code below, written for Script Lab, contains the full Word Open XML. The current selection in the document is retrieved and the Word Open XML assigned to this range using the insertOoxml method; the paragraph’s content will be replaced. Then the text is assigned to the range. Finally, the queued commands are synched to the document.

Note that, even though Word Online does not support tabs and tab stops, this code will run there without error. Neither the tab stop nor the tab characters will be written to the document, however! In a future post, I’ll show how to detect whether the code is running in Word Online.

//create paragraph with tab stops and tabs
async function run() {
    await Word.run(async (context) => {
        let sXml = 
'<pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage">
<pkg:part pkg:name="/_rels/.rels" 
pkg:contentType="application/vnd.openxmlformats-package.relationships+xml" 
pkg:padding="512"><pkg:xmlData><Relationships xmlns=
"http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type=
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" 
Target="word/document.xml"/></Relationships></pkg:xmlData>
</pkg:part><pkg:part pkg:name="/word/document.xml" pkg:contentType=
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml">
<pkg:xmlData><w:document 
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body><w:p><w:pPr><w:tabs>
<w:tab w:val="left" w:pos ="851"/></w:tabs></w:pPr>
<w:r><w:t></w:t></w:r></w:p></w:body>
</w:document></pkg:xmlData></pkg:part></pkg:package>';

        //console.log("XML: " + sXml);
        let para = context.document.getSelection();
        para.insertOoxml(sXml, "Replace");
        para.insertText("CC:\tPerson1\n\tPerson2", "Start");
        await context.sync();
    });
}

Leave a Reply