Inserting a base64 encoded image into a Word 2016 document

In this article I will demonstrate the new Word JavaScript API method which can be used to insert a base64 encoded image into a Word 2016 document.

Introduction

In the latest version of Office 365 Word 2016 (May 2016) a new capability has been released “insertInlinePictureFromBase64”. The documentation so far is a little sparse but can be found here (http://dev.office.com/reference/add-ins/word/body) . The new method is part of the body object within the JavaScript API model. For more information on the version of Word you need to get this to work check out this link (Office add-in requirement sets).

Do you have the correct version?

I found this StackOverflow answer which gave code similar to the following to test to see if you have the correct version of the JavaScript API available.

Word.run(function (context) {
	// Create a proxy object for the document body.
	var body = context.document.body;
	// Queue a commmand to insert HTML in to the beginning of the body.
	if (Office.context.requirements.isSetSupported("WordApi", "1.2")) {
		body.insertHtml('<strong>"You have the right version"</strong>', Word.InsertLocation.start);
	} else {
	body.insertHtml('<strong>"You do not have the right version. This functionality requires Word with at least January 2016 update!! (check  builds 6568+)"</strong>', Word.InsertLocation.start);
	}
		// Synchronize the document state by executing the queued commands,
		// and return a promise to indicate task completion.
		return context.sync().then(function () {
			console.log('HTML added to the beginning of the document body.');
	});
})

I created a simple Add-In examples locally, containing a message DIV and some FirebugLite.

Inserting an image.

Taking one of the examples from the documentation page and modifying it slightly we can see the basics of how the insert will happen:

var img = '' //a base64 encoded string

// Run a batch operation against the Word object model.
Word.run(function (context) {

    // Create a proxy object for the document body.
    var body = context.document.body;

    // Queue a command to insert the image.
    body.insertInlinePictureFromBase64(img, 'End');

    // Synchronize the document state by executing the queued commands,
    // and return a promise to indicate task completion.
    return context.sync().then(function () {
        app.showNotification('Image inserted successfully.');
    });
})
.catch(function (error) {
    app.showNotification("Error: " + JSON.stringify(error));
    if (error instanceof OfficeExtension.Error) {
        app.showNotification("Debug info: " + JSON.stringify(error.debugInfo));
    }
});

As we can see from the code, what is happening is the the “context” of the Word document is being manipulated in memory and then “sync’ed” with the actual HTML managing the Word document. The “Word document” itself is not a webpage. It can handle HTML but it is not a web browser and is not capable of being manipulated directly from the Add-In. If this works we should see an image.

What is a base64 encoded string anyway?

Base64 is a group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. The term Base64 originates from a specific MIME content transfer encoding.” (https://en.wikipedia.org/wiki/Base64)

If you look for examples of base64 encoded images on the web you can see something like this – https://css-tricks.com/examples/DataURIs/ which shows two ways of referencing an image using the encoded string. Firstly in CSS and secondarily as an image. View the source of the page

i2

i1

The “base64 encoded string” takes the form “data:”+ [content-type]+”;base64,” and then the actual string

data:image/gif;base64,R0lGODlhEAAQA……………

“data:”+”image/gif”+”;base64,”R0lGODlhEAAQA……………….”

I am not using the full string for the base64 encoded image in my code snippets, because it is unwieldy in the context of the blog post.

THIS IS THE REALLY IMPORTANT PART !!!!
In the case of insertInlinePictureFromBase64 the entire string does not work! You only use everything after the comma.
THIS IS THE REALLY IMPORTANT PART !!!!

In my case only the following is necessary

var img = ‘R0lGODlhEAAQA……

In Summary

As you can see from the image below when you put this all together inside your JavaScript you can insert images into your Word document..

i3

Using the Jellyfish image example (which is a really long text string)

i4

 

Conclusion

In this article we seen how the new body.insertInlinePictureFromBase64 method can be used with the Word API to insert an image into a word document. Particular attention has to be paid to the string being inserted for the image, it must not include the content-type or the data signifier.

In the next article we will look at how we can easily a base64 encoded image for ourselves programmatically in the context of an Add-In

 

 

Advertisements

Speaking at the Collab365 Summit 2016 – May 11th 2016 – “Office 365 Add-Ins – a web developer’s playground”

I delighted to say that at very short notice, I am speaking at the Collab365 Summit virtual conference on May 11th 2016.

https://collab365.events/collab365-summit-2016/

The presentation will be recorded and there will be a live chat session while it is being broadcast

Monday May 11th 12pm CST

co1

Title
Office 365 Add-Ins – a web developer’s playground

Abstract
A web developer’s exploration into the capabilities and integration opportunities exposed by O365 and the Office Add-In model. Like most office workers, we all spend a significant amount of time in our “Microsoft Office” productivity tools. Even email is still a productivity tool. Productivity starts to diminish though if we have to move outside of our Office environment and hunt for information and/or complete business workflow processes.

With the creation of Office 365 Add-Ins, Microsoft has presented web developers with a new opportunity to create rich, engaging and integrated user experiences without having to leave the “experience” of our Office applications. Developers have the ability to create Add-Ins using HTML/JS/CSS and these run in the Windows Client, on the web, on our phones and even on the OS X desktop client.

In this presentation Mark will provide lots of demonstrations of how to get started with Office Add-Ins. These will include: creating your first Add-In in under 2 minutes, how to simplify workflow approval without having to leave your email client, how to pull report and analytics data into your Office product suite applications and how to integrate your content with cognitive analytics. All of this, written without a single line of C#.

Come to the presentation and find out why Office 365 Add-Ins are a modern web developer’s playground.

Office 365 Add-Ins Dialog API released

In this article I will briefly touch on the new Dialog API, the fact that it works in Outlook and that you need to have the most recent version of Office 2016 to get this to work.

Introduction

In his recent article Simon Jaeger published information about the new Office 365 API “DialogAPI” functionality

http://simonjaeger.com/lets-have-a-dialog-about-the-dialog-api/

This is a really nice UX item added to the Office 365 Add-In suite of functionality. If I need to ask the user to interact with the application and/or make a decision, it is now available right there from within the Add-In.

Not just for Word

While Simon’s example was created in Word, using the following code it was really simple to test and see if it was available in Outlook as well.

It is !!

d1

I used my firebuglite Add-In example to quickly load up an existing Add-In and paste the code into the console window. Took 2 minutes to verify which was great.

        var dialogUrl = location.href

        // Display the dialog.
        Office.context.ui.displayDialogAsync(dialogUrl, { width: 15, height: 27, requireHTTPS: true }, function (asyncResult) {
            if (asyncResult.status !== Office.AsyncResultStatus.Succeeded) {
                // TODO: Handle error.
                return;
            }

            // Get the dialog and register event handlers.
            var dialog = asyncResult.value;
            dialog.addEventHandler(Microsoft.Office.WebExtension.EventType.DialogMessageReceived, function (asyncResult) {
                if (asyncResult.type !== Microsoft.Office.WebExtension.EventType.DialogMessageReceived) {
                    // TODO: Handle unknown message.
                    return;
                }

                // Parse the message.
                var data = JSON.parse(asyncResult.message);
                showNotification('Hello #Office365Dev', data.name);

                // TODO: Do something with the data.

                // We got our data, time to close the dialog.
                dialog.close();
            });
        });

In Simon’s post he also referenced the Channel 9 video created to go along with the announcement. In that the presenters demo how to use the new dialog API to Authenticate to O365 and return the token back to the Add-In. This is a more elegant method than what I have been using with the whole new IE window opening. I will be looking into combining the methods for my own nefarious use in the near future….. 🙂

Office Version (April 2016)

I wasted over a day being frustrated with why my code would not worked, and it turns out that my version of Office was not up to date. You need to go to the following site to download the latest version of Office (https://github.com/OfficeDev/Office-Add-in-Commands-Samples/blob/master/Tools/LatestOfficeBuild.md).

 

Conclusion

The new Dialog API presents a new option for developers in their continued ability to bring functionality into the Office 365 environment.

 

Getting started locally with a basic O365 Word Add-In (no web hosting)

In this article I will demonstrate how to add a local shared drive to your Trust center which will facilitate developing basic Add-In functionality locally. This will not works for Add-Ins which require internet access (c:\local cannot call https) but for testing out your Word API skills this is perfect.

Introduction

Using the example set out on Word Add Ins (https://github.com/OfficeDev/office-js-docs/blob/master/word/word-add-ins.md) I was able to start playing with the Word JavaScript API locally. But at times I did not have a network share to map my add in with. With a little searching though I found that you can map a local file with \\localhost.

Taking from the example (which you should read first) I am going to modify the section about adding a trust center with an example.

My files are located at c:\Users\mroden\WebstormProjects\Azure\xomino365\localWordDev

Adding the Trust Center

Within Word 2016 select

  • File
  • Options
  • Trust Center
  • Click the Trust Center Setting Button
  • Select the Trust Center Add-Ins option on the left

In the Catalog URL bar I add \\localhost\C$\Users\mroden\WebstormProjects\Azure\xomino365\localWordDev and “Add Catalog”

w5

The Catalog is added as a network share

w6

make sure you check the “Show in Menu” option and restart Word

Using the example manifest

Using the example manifest provided in the Github Project (link again) and my own GUID I was able to create a manifest to my local files like this

 

<?xml version="1.0" encoding="UTF-8"?>
    <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="TaskPaneApp">
        <Id>0646c7a1-c876-482b-9119-e0f25c30b610</Id>
        <Version>1.0.0.0</Version>
        <ProviderName>Microsoft</ProviderName>
        <DefaultLocale>en-US</DefaultLocale>
        <DisplayName DefaultValue="Boilerplate content" />
        <Description DefaultValue="Insert boilerplate content into a Word document." />
        <Hosts>
            <Host Name="Document"/>
        </Hosts>
        <DefaultSettings>
            <SourceLocation DefaultValue="c:\Users\mroden\WebstormProjects\Azure\xomino365\localWordDev\App\Home\home.html" />
        </DefaultSettings>
        <Permissions>ReadWriteDocument</Permissions>
    </OfficeApp>

Using that I was then able to run the example code. There were a couple of warning though to accept to get this to work.

w1

w2

w3

There we have it – the locally hosted example working.

w4

Using the technique to add firebuglite to the Add-In I was then able to start to copy an paste example code from Microsoft examples and start to learn the Word API and its nuances.

I am also found this Office Snippets site which was GREAT to get started as well

http://officesnippetexplorer.azurewebsites.net/#/add-in/word

Conclusion

Being able to map a localhost as a “network drive” allowed me to get started without the need to constantly check code into my local Git repository and sync it with Azure

Productive !!!

Office Add-In problem solved: Internet Explorer by default does not allow CORS

In this article I will highlight an issue you may come across when you are trying to deploy an Office Add-In pulling data from a different domain.

Introduction

As my adventure into Office Add-Ins has continued I began to look at integrating data from outside of the O365 environment, into Office Add-Ins. In this example specifically pulling data from an external source into an Excel spreadsheet.  I followed the example code from the Build your first Excel add-in site and as I showed in the Firebug Lite post I was able to take the example code and make it work in an Add-In. I then wanted to prove to myself that I could pull it from a real data source.

The Data Source

The data source was a Domino web service which I was able to duplicate the data feed from. As you can see from the response headers I am adding everything necessary to allow CORS. I was hosting the Add-In at napacloudapp.com and looking to pull the data from copper.xomino.com

d1

But every time I ran the code I had an error which told me that the data could not be pulled from copper. For the life of me I could not figure out how until I came across a post on stackoverflow (which I cant find to reference now) which pointed me in the direction of IE security settings.

By Default CORS is turned off in Internet Explorer !

This is a setting which goes WAY back to the start of Cross Site Scripting issues and way before CORS itself was even a standard. This was actually something Microsoft did right with old IE !

If you go to Tool – Internet options – Security and look inside the Custom Settings option for Internet sites you will find that accessing data across domains is Disabled! So CORS isn;t even an options – cross domain data retrieval is off.

d2

The solution – Trusted sites

You can change this for Internet sites in IE10 or IE11 because both of those browsers have CORS built in to protect you – not so much in IE9.

In a business environment though you should be using Trusted Sites. If your Add-In HOST website (www.napacloudapp.com in my case) is added to Trusted Sites then the Access data across domain options is set to “Enable” and the problem is fixed.

Conclusion

This was something I first encountered way way back over 10 years ago when I was messing with XML Islands back in IE5 ! I knew it was there but had never really though about it in this context.

Because the client based Add-In experience is basically an embedded Internet Explorer instance, all the browser settings are carried into the experience with it.

As is often the case, once you understand the problem, the solution is relatively simple – it’s just getting there which is bothersome 🙂

 

 

 

 

Caching data across disparate O365 Add-Ins (and across client types)

In this article I will show how data can be cached between O365 office Add-Ins. This also includes different clients (Outlook, Excel etc, even Internet Explorer)

Introduction 

In the previous article we looked at how we are able to cache data within an Office Add-In using the browser’s document.cookie capability. In this article I will demonstrate how flexible this capability is and how it will even work across different Add-Ins and different applications.

Creating a Cookie

Cookies can be created and be stored within a specific domain/path and this is critical for this example to work. If you do not add a path variable to the cookie when creating it, it will not be available outside of the specific URL from whence it was created.

Going back to the setCookie code I used before:

function setCookie(name, value, duration) { 
  var now = new Date();
  var time = now.getTime();
  var expireTime = time + 1000*duration;
  now.setTime(expireTime);
  document.cookie = name+'='+value+';expires='+now.toGMTString()+';path=/';
}

We can see that there is a path=/ variable added in the creation. This statement means that this cookie is available to all web pages within the domain. Without it, the cookie is tied to the exact URL it was created from. Ideally you would not use the whole domain and you would be more specific to a single directory path. For the sake of this example though it makes life easier 🙂

What this means for us though is that we are able to create a cookie from one Add-In (URL Host) and as long as it is in the same domain, we can access that cookie value in another.

Creating the cookie in one Add-in

In the example below we are setting the cookie in the Outlook Add-in. The location.href  (which is the host page for the Add-In) is https://copper.xomino.com/xomino/extjs.nsf/O365Token/index.html. You can see from the Firebug Lite console that Outlook has appended the client name to the end of the URL.

c6

Using the setCookie function we create the “EXAMPLEToken” cookie and make it valid for 120 seconds.

We then open Excel and look at an Add-In I created there. As you can see from the example below the location.href for the hosted Add-In file is https://copper.xomino.com/xomino/O365.nsf/index.html. While the path is not exactly the same as the Outlook example – the hosting domain is (copper.xomino.com).

c7

The example above uses a getCookie function to get the value of the “EXAMPLEToken” and our value “xomino365” is returned successfully.

After two minutes a subsequent attempt to get the cookie fails because it has expired

c8

And it is as simple as that.

Conclusion

Adding a path variable to the cookie creation has allowed us to create the cookie within on Add-In (Outlook in this case) and access it from within another Add-In (Excel in the second case).

Caveat

Be aware that you need to have control and consideration about how much data you load into a cookie. Any Internet Explorer cookie for a given domain cannot be more than 10234 chars.

Addendum

What is actually interesting to find out is that even though the embedded client Add-In used Internet Explorer – any cookies created within the client are actually available within the stand alone Internet Explorer – and Vice Versa!

The cookie created in outlook before – is available in full Internet Explorer as well.

c9

This has to raise some fascinating potential for a truly immersive experience between users in the outlook clients and their web browsing. Here it is being set in IE and being read in the Excel Add-In

c10

 

 

Caching data in O365 Add-ins using document.cookie

In this article we will look at a technique for caching data (ultimately an OAuth token) within an Office Add-in.

Introduction

In previous articles I have discussed how to create an Authorization token in an Office Add-in and created the O365Token project on Github. These set up the possibility for using only JavaScript (no C#) to generate an Azure AD Authorization token which can then be used to interact with the O365 environment.

The only drawback so far is that every time the Add-In loads, a new window pops up to collect the token. It would be a much more elegant solution to be able to cache the token somehow and then re-use it. In this article we will look at using cookies for “caching” tokens in both the Outlook Client and the O365 web mail experience.

 

Cookies

Cookies have been around for almost as long as web browsers. They can be set to hold a value for a finite amount of time (permanent) or fixed to only this browser session (session).

Using the FireBug Lite technique previous described we are able to take a look at cookies and how they are stored within the browser.

Within Outlook itself we can see the cookies using document.cookie

c1

and we can set a cookie like this

c2

Setting an expire date

So that’s all well and good but if we actually want to store an OAuth token we want the cookie to be valid for no longer than the token itself. So then we have to set an expire date when we create the cookie. This is achieved like this:

function setCookie(value, duration) { 
  var now = new Date();
  var time = now.getTime();
  var expireTime = time + 1000*duration;
  now.setTime(expireTime);
  document.cookie = 'OAuthToken='+value+';expires='+now.toGMTString()+';path=/';
}

And in FireBug we can see this in action – in this case we set the cookie for 10 seconds, and immediately log it to the console

c3

and then more than 10 seconds later we just test the document.cookie and the OAuthToken is gone

c4

OAuth token generation

When requesting the OAuth token for O365, the token returned is valid for 3600 seconds (an hour). So if we cache the cookie for an hour it will be a good indication (by its existence) that it is still valid. If there is no cookie – we have to get a new token.

Conclusion

In this article we have seen how through the use of the browser’s cookies, we are able to potentially store data. In a future article we will look at how to store and reuse an OAuthToken