locked
Rendering Microsoft Office documents in mobile devices RRS feed

  • Question

  • User1314407993 posted

    My application allows users to upload various documents, assign an approver and (optionally) reviewers. The core requirement is that it supports PDF files, with Word documents being an extremely "nice to have". The PDF wasn't much of a problem to deal with, as this is an internal application, and we can require users use Chrome for it. Still, there was something I learned that might hopefully be useful to someone else. The goal is for the PDF, Word, or plain text document to be rendered in the browser. To achieve this, I needed to convert the document to a Base64 string. I used Sautinsoft's DocumentCore to load the Word doc from blob storage and into memory. The PDF was even easier. I have an extension method that takes a Stream and writes it to a byte array before converting it to a Base64 string. I was rendering it to the browser with an object tag:

    <object data="data:@Model.ContentType;base64, @Model.DocumentBase64String" type="@Model.ContentType" style="width: 100%; height: 100%;"></object>

    But I ran across an odd problem that was surprisingly difficult to fix... when downloading the file from the browser, I get the Chrome PDF viewer tools to download the file, but the name is always "download.pdf" or something like that. Trying to modify this value on the fly when downloading the PDF file was a fruitless endeavor. The problem was that the document source is a Base64 string generated by a MVC method - it is NOT a Url to a static file hosted on some server. I retrieve the document from blob storage, read the blob into memory, where it's converted as needed. Chrome's PDF viewer requires a static file format to get the filename correctly set when the file is downloaded. I had to get a little creative to get this to work, which is what I wanted to share...

    First, the PDF is now rendered in an embed tag with the Url that generates the PDF:

    <embed src="/Home/GetPdf/@Model.Filename?guid=@Model.Guid" width="100%" height="100%" type="application/pdf"></embed>

    Notice the filename at the end of the Url, just before the querystring. I don't use this value in the Controller method. It exists only to make Chrome's PDF viewer set the filename correctly. The only value I actually need is the Guid. Here's the Controller method:

    public IActionResult GetPdf(Guid guid)
    {
        // Get document record from database - will have Guid to look up in blob storage. 
        var documentApproval = _context.DocumentApproval.FirstOrDefault(ca => ca.Guid == guid);
    
        // Get the BlockBlob object that holds the PDF, using the Guid identifier and document status. 
        var blockBlob = BlobStorageMgmt.GetBlobItem(guid, (DocumentStatus)documentApproval.StatusId);
        blockBlob.FetchAttributes(); // Need to call this method to retrieve Content Disposition. 
    
        // MemoryStream pulls in the PDF data as a byte array.
        var ms = new MemoryStream();
    
        blockBlob.DownloadToStream(ms); // Performs the pull to load data as byte array.
        ms.Position = 0; // Reposition to "0" - if not, stream will be at the end - not good!
    
        // Return as FileStreamResult, passing in MemoryStream object "ms" and the Content Disposition. 
        return new FileStreamResult(ms, blockBlob.Properties.ContentType);
    }

    And there it is. Works like a charm. This is something to keep in mind for times when you need a Url format that mimics a hosted static file. 

    OK, now I need the Word document viewer to work as well. I'm using the object tag I posted above to load Word documents. The embed tag gave me some grief, and I don't know if it will handle the documents at all. It's fine - the object tag works very well. Even as a Base64 string, the document renders with all styles and graphics. My problem is two-fold. The first problem is minor and already handled, more or less - with either the embed or object tag, giving the containing div a width and height of 100% results in the width being 100% of the current row, but for height I get a couple hundred pixels, which isn't even close. I'm using Bootstrap 4, and this is my markup:

    <div class="row document-row">
    
        <div class="col-sm-12 text-center">
    
            <div id="DocumentContainer">
    
                @if (Model.ContentType == "application/pdf")
                {
                    <embed src="/Home/GetPdf/@Model.Filename?guid=@Model.Guid" width="100%" height="100%" type="application/pdf"></embed>
                }
                else
                {
                    // Handles Word documents and text files.
                    <object data="data:@Model.ContentType;base64, @Model.DocumentBase64String" type="@Model.ContentType" style="width: 100%; height: 100%;"></object>
                }
    
            </div>
    
        </div>
    
    </div>

    I wasn't sure how to get the container div for the document viewer to fit the size of a single page, so I used media queries in my CSS to set the height and width explicitly for different screen sizes. Not sure if there's a better solution, but that's what I knew how to do. 

    The other problem is far more important... although the PDF viewer will scale to smaller screen sizes all the way down to smartphones, the object tag with a Word document does not. The Word document will decrease in width until the screen resolution is below 1200px, and then there's a horizontal scrollbar for the Word doc, which I hate.

    side note: Also, the resizing doesn't happen automatically even for the PDF viewer. I have to reload the page after collapsing Chrome to a smaller screen size. This will likely never be a problem, since users aren't going to be resizing their browsers at a desktop to the size of a smartphone, but it's something to keep in mind. 

    I want the Word doc to reduce in size, keeping its aspect ratio as it does so, all the way down to smartphone size. I want something similar to what I get with the PDF viewer, which only provides a vertical scrollbar, which I'm fine with, as the documents will often have multiple pages. I know it's not just the object tag causing the issue, because if I try this with a plain text file, it reduces in size similarly to the PDF. Word seems like the document has a minimum size, and won't reduce any farther.

    Please let me know if there's a good way to achieve this.

    Wednesday, July 24, 2019 5:02 PM

All replies

  • User-474980206 posted
    Your title implies you want mobile support, but I don’t believe chrome mobile support embed or object tags. If you are testing on desktop with chrome, use debug to go into mobile device mode for testing, the embed should stop working.

    Mobile browsers can display a pdf link, but word is more problematic. The typical solution is to convert the word document to a pdf, or use an online word editing site (like google docs).

    Thursday, July 25, 2019 12:31 AM