How can I show an pdf file in xamarin pcl uwp that is outside my instal folder?

All we need is a simple explanation of the problem, which is provided below.

Hy, I’m working in a Xamarin PCL project with the platforms Android and UWP. As a feature the user should be able to open an pdf file.

For this I’m using Mozilla pdf.js. I have followed this link to get it done on Android and UWP.
https://developer.xamarin.com/recipes/cross-platform/xamarin-forms/controls/display-pdf/
Only for UWP I can’t get it to function.

Here is my custom renderer for UWP

[assembly: ExportRenderer(typeof(PdfView), 
typeof(PDF.UWP.Renderers.PdfViewRenderer))]
namespace PDF.UWP.Renderers
{
/// <summary>
/// The asset folder of the UWP app must contain the pdfjs folder and files.
/// </summary>
public class PdfViewRenderer: WebViewRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null)
        {
            PdfView pdfView = Element as PdfView;
            string sFile = string.Format("ms-appx-web://{0}", WebUtility.UrlEncode(pdfView.Uri));
            Control.Source = new Uri(string.Format("ms-appx-web:///Assets/pdfjs/web/viewer.html?file={0}", sFile));
        }
    }
}
}

Here is my PdfView class.

public class PdfView: WebView
{
    public static readonly BindableProperty DocumentInfoProperty =
        BindableProperty.Create(propertyName: nameof(TheDocumentInfo), returnType: typeof(DocumentInfo),
            declaringType: typeof(PdfView), defaultValue: default(DocumentInfo));

    public DocumentInfo TheDocumentInfo
    {
        get { return (DocumentInfo)GetValue(DocumentInfoProperty); }
        set { SetValue(DocumentInfoProperty, value); }
    }

    public string Uri { get { return TheDocumentInfo.LocalUrl; } }
    public string FileName { get { return TheDocumentInfo.FileName; } }
}

The file location on uwp =
“ms-appx-web://C%3A%5CUsers%5CUser%5CAppData%5CLocal%5CPackages%5CPDFTEST.UWP_v4j5n0js0cwst%5CLocalState%5CPDFTest.pdf”

And this is correct.

But the error is:

Message: Unexpected server response (0) while retrieving PDF
“ms-appx-web://C:/Users/User/AppData/Local/Packages/PDFTest.UWP_v4j5n0js0cwst/LocalState/PDFTest.pdf/”.

UPDATE

I have recreated the renderer to:

protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null)
        {
            // TODO: testen
            PdfView pdfView = Element as PdfView;
            string sFile = string.Format("ms-appx-web://{0}/{1}", pdfView.Uri.Replace(pdfView.FileName, ""), WebUtility.UrlEncode(pdfView.FileName));
            Control.Source = new Uri(string.Format("ms-appx-web:///Assets/pdfjs/web/viewer.html?file={0}", sFile));
        }
    }

I don’t know if this is the solution to my problem but this error is gone.
The error now is:

PDF.js v1.1.366 (build: 9e9df56)

Message: stream must have data

UPDATE (I don’t know if u should add this in this question or if I should made another one).

My error is still

stream must have data

I know now why. Because I’m developing a UWP application and I want to access a file outside my instal folder. This location is

C:\Users\User\AppData\Local\Packages\PDFTest.UWP_v4j5n0js0cwst\LocalState\

Apperently I can’t access files outside my instalation folder. This includes coping the file to my install folder and read it there.

UPDATE
I have 2 versions of pdf.js in my project. (1.1.366 and 1.9.426)
This is my code now

    protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null)
        {
            PdfView pdfView = Element as PdfView;
            var uriString = "ms-appdata:///local/" + WebUtility.UrlEncode(pdfView.FileName);
            Control.Source = new Uri(string.Format("ms-appx-web:///Assets/pdfjs/web/viewer.html?file={0}", uriString));
        }
    }

When I try to open the file with Launcher.LaunchFileAsync and use the uriString it opens my browser and shows me the file.

In my application I get the following error’s

(v1.1.366) Stream must have data.

(v1.9.426) Unexpected server response (0) while retrieving PDF “ms-appdata:///local/PDFTest.pdf”.

I know that the pdf uri is correct and accessible but it still doesn’t work.
(for v1.9.426 I have added in viewer.js

var HOSTED_VIEWER_ORIGINS = [‘null’, ‘http://mozilla.github.io‘, ‘https://mozilla.github.io‘, ‘ms-appdata://’, ‘ms-appx-web://PDFTest.uwp’];)

link to the testproject

Let’s Solve it:

This is a common error many developers questioned us about it. So we write the explanation above. You just have to apply the suggested solution to your code and it will do for you. If you still getting this error after applying this code then comment below we will get back to you with the new method.

Solution 1

I know now why. Because I’m developing a UWP application and I want to access a file outside my instal folder. This location is

You have use ms-appx-web: uri scheme as your pdf file access path. Actually, your pdf path is C:\Users\User\AppData\Local\Packages\PDFTest.UWP_v4j5n0js0cwst\LocalState\ that stored in the app’s local folder. So the file will not be accessed.

I have also tested with “ms-appdata:///local/” uri scheme to access the pdf file base on your project. Unfortunately, It can’t be recognised by viewer.js.

And then, I tried to convert pdf file into Base64String then opening it by calling the openPdfAsBase64 JS function in the viewer.js.

private async Task<string> OpenAndConvert(string FileName) 
{
    var folder = ApplicationData.Current.LocalFolder;
    var file = await folder.GetFileAsync(FileName);
    var filebuffer = await file.OpenAsync(FileAccessMode.Read);
    var reader = new DataReader(filebuffer.GetInputStreamAt(0));
    var bytes = new byte[filebuffer.Size];
    await reader.LoadAsync((uint)filebuffer.Size);
    reader.ReadBytes(bytes);
    return Convert.ToBase64String(bytes);  
}

Usage

protected  override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
    base.OnElementChanged(e);
    if (e.NewElement != null)
    {   
        Control.Source = new Uri("ms-appx-web:///Assets/pdfjs3/web/viewer.html");
        Control.LoadCompleted += Control_LoadCompleted;
    }
}

private async void Control_LoadCompleted(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
    CustomWebView pdfView = Element as CustomWebView;
    if (string.IsNullOrEmpty(pdfView?.Filename)) return;

    var ret = await OpenAndConvert(pdfView?.Filename);       

    var obj = await Control.InvokeScriptAsync("openPdfAsBase64", new[] { ret });
}

You could use this viewer.js that was added openPdfAsBase64 method.

Got hints following: How to embed the PDFjs into your C# Project.

Note: You are free to use these solutions for your personal use. We recommend you apply the first solution to your code because it was tested in our system before posting it on this page.

We are always trying to help the developer community, So we made their work easy. Basically, we collected these data from stackoverflow.com, As it is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0.

Leave a Comment