Create Multipage Files Using the Xamarin Camera Control and LEADTOOLS Cloud Services

Posted on 2019-06-05 Nick Villalobos

Within the past half year, LEADTOOLS has had some major releases with the SDK and the Cloud Services. Today, we are going to talk about both the Xamarin Camera Control as well as the Merge Web API. We will be using the Xamarin Camera Control to take multiple pictures and then send those image to LEAD's cloud services. Once those have been uploaded, the Merge Web API will merge all those images into one single file.

Applying both of these technologies has limitless potential for applications across various industries. For instance, say an insurance company requires pictures of damaged property for proof of damages. Instead of sending 20 individual images, you can create one file that includes those 20 images.

Another use case would be for anyone needing to digitize documents. If you are trying to make space and clear out old documents, then you would be able to take a picture of all your documents and then combine them to one single image.

Below is a tutorial on how you can achieve this in a Xamarin application. This example is great for developers who want to access a mobile device's camera in just a couple lines of code without having to using native iOS/Android code. It's also great for those wanting to use a WebAPI and a cloud service without having to worry about the hassle of maintaining a server. Check out the tutorial and see how easy it is to make use of both the Xamarin Camera Control and the LEADTOOLS Cloud Services in one application.

[gfycat data_id="BriskMeekLice"]

The video above is a quick demonstration of the application in action. As you can see, I take pictures using the Xamarin Camera Control and then click "Merge" to merge the four files together. The output for this example is PDF, but that can be changed in the code.




Download Project

Setting up Your App

The first step is to retrieve your free 30-day evaluation license by registering at https://www.leadtools.com/downloads/nuget and create an account for your free trial of LEADTOOLS Cloud Services at https://services.leadtools.com/account/register.

Once you have a LEADTOOLS license, open Visual Studio 2017 and create a new project. First, add the Leadtools.Camera.Xamarin assembly in the MainPage.xaml.

xmlns:leadtools="clr-namespace:Leadtools.Camera.Xamarin;assembly=Leadtools.Camera.Xamarin"

Now replace the default auto-generated label within the StackLayout with the Xamarin CameraView, as well as a button that will be used to take the picture and one that will merge all the images that have been taken.

<StackLayout>
	<leadtools:CameraView x:Name="leadCamera" CameraOptions="Rear" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>
	<Button x:Name="snapBtn" HorizontalOptions="FillAndExpand" Text="Snap Picture" Clicked="SnapClicked" />
	<Button x:Name="mergeBtn" HorizontalOptions="FillAndExpand" Text="Merge" Clicked="performMerge" />
	<Button x:Name="downloadFileBtn" HorizontalOptions="FillAndExpand" Text="Go to file!" Clicked="downloadFile"/>
</StackLayout>

For more of an introduction to using the LEADTOOLS Camera Control for Xamarin as well as setting the LEADTOOLS license, check out the previous post, Finally a Camera Control for Xamarin. Please note that the previous article is necessary to complete before continuing.

Now that the Xamarin Camera Control has been added and the license has been set, add the code to take the picture. The CameraView Class has an event called PictureReceived which will be fired when an image is captured after calling ICamera.TakePicture().

The call to the TakePicture method will go in the button click event.

void SnapClicked(object sender, EventArgs args)
{
    snapBtn.IsEnabled = false;
    leadCamera.Camera.TakePicture();
}

To create the event handler for PictureReceived, add the following line of code after InitializeComponent():

leadCamera.PictureReceived += LeadCamera_PictureReceived;

The Code

First, create some global variables and create a new class.

private static HttpClient client;
private List<MergeRequestObject> MergeImages = new List<MergeRequestObject>();
private string hostedServicesUrl = "https://azure.leadtools.com/api/";
private static string fileUrl;
public class MergeRequestObject
{
    public string FileId { get; set; }
}

As mentioned above, and from our previous article, MainPage.xaml.cs has everything needed to start the Xamarin Camera Control. On top of that, add code to auto-rotate the image, enable the merge button, disable the download button, and an InitClient method. You should have the following below InitializeComponent().

leadCamera.CameraOptions.AutoRotateImage = true;
leadCamera.PictureReceived += LeadCamera_PictureReceived;
mergeBtn.IsEnabled = false;
downloadFileBtn.IsEnabled = false;
InitClient();

InitClient is needed for authenticating your application with the LEADTOOLS Cloud Services.

private void InitClient()
{
    // Once you created your app in your account https://services.leadtools.com/manage 
    // you will receive an email with the AppId and Password
    string AppId = "Enter AppID Here";
    string Password = "Enter SecretKey Here";

    client = new HttpClient
    {
        BaseAddress = new Uri(hostedServicesUrl)
    };

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    string authData = string.Format("{0}:{1}", AppId, Password);
    string authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
}

The code inside PictureReceived will be for saving each image that has been taken and then uploading them to the LEADTOOLS Cloud Services. In this example, the images will be stored in a MemoryStream as a PNG. After the image is saved, it will need to be uploaded to the LEADTOOLS Cloud Services. To do that, a method called UploadForMerge will be used. This method will need the memory stream and will return a GUID. Once you have the GUID, it will be stored in a list, List<MergeRequestObject> mergeImages. The MergeRequestObject is a class that we created that contains a string called FileID. FileID will now be the GUID that was returned from UploadForMerge. Each image that is taken will go through this process and will have its own GUID that will be added to the list.

private void LeadCamera_PictureReceived(FrameHandlerEventArgs e)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        snapBtn.Text = "Image is uploading to LEADTOOLS Cloud Services, please wait";
        mergeBtn.IsEnabled = false;
    });

    using (MemoryStream ms = new MemoryStream())
    {
        using (RasterCodecs codecs = new RasterCodecs())
        {
            codecs.Save(e.Image, ms, RasterImageFormat.Png, 0);

            var id = UploadForMerge(ms);

            MergeImages.Add(new MergeRequestObject { FileId = id.ToString() });

            Device.BeginInvokeOnMainThread(() =>
            {
                snapBtn.IsEnabled = true;
                snapBtn.Text = "Snap Picture";
                mergeBtn.Text = $"Merge {MergeImages.Count} file(s)";
                DisplayAlert("Image saved!", "Image has been uploaded", "OK");
                mergeBtn.IsEnabled = true;
            });
        }
    }
}

The UploadForMerge method is where the image is getting sent to the LEADTOOLS Cloud Services and then returning a GUID.

public Guid UploadForMerge(MemoryStream uploadForMergeStream)
{
    HttpContent byteContent = new ByteArrayContent(uploadForMergeStream.ToArray());

    using (var formData = new MultipartFormDataContent())
    {
        formData.Add(byteContent, "imageStream");

        var url = "UploadFile?forMerge=true";
        var res = client.PostAsync(url, formData).Result;
        var guid = Guid.Empty;

        if (res.IsSuccessStatusCode)
        {
            var id = res.Content.ReadAsStringAsync().Result;
            return Guid.Parse(id);
        }
        else
            return Guid.Empty;
    }
}

Once all the pictures have been taken and are uploaded to the LEADTOOLS Cloud Services, it’s time to merge them to a single file. For this, create a new method called PerformMerge.

void performMerge(object sender, EventArgs args)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        mergeBtn.Text = "Merging files, please wait";
        snapBtn.IsEnabled = false;
        mergeBtn.IsEnabled = false;
    });

    var id = PostMerge();

    if (id == Guid.Empty)
        Device.BeginInvokeOnMainThread(() =>
        {
            DisplayAlert("Error", "GUID is empty", "OK");
        });

    var results = Query(id.ToString());

    // Parse results and add the URL to the fileUrl button
    JArray array = JArray.Parse(results);
    foreach (var requestReturn in array)
    {
        var UrlArray = JArray.Parse(requestReturn.SelectToken("urls").ToString());
        foreach (var uri in UrlArray)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                fileUrl = uri.ToString();
            });
        }
    }

    // Clear list to create a new single file
    MergeImages.Clear();
    Device.BeginInvokeOnMainThread(() =>
    {
        downloadFileBtn.IsEnabled = true;
        mergeBtn.Text = "File(s) have been merged";
        snapBtn.IsEnabled = true;
    });
}

Inside PerformMerge is another method called PostMerge method which will also return a GUID.

public Guid PostMerge()
{
    var stringContent = new StringContent(JsonConvert.SerializeObject(MergeImages), Encoding.UTF8, "application/json");
    // Format 4 will save merged files to a PDF
    var url = $"Conversion/Merge?Format=4";
    var res = client.PostAsync(url, stringContent).Result;

    if (res.IsSuccessStatusCode)
    {
        var id = res.Content.ReadAsStringAsync().Result;
        return Guid.Parse(id);
    }
    else
        return Guid.Empty;
}	

Continuing with the PerformMerge method, this is where the results will be returned from using the ID returned from PostMerge and then queried using a new method called Query which takes in a string and returns results.

private string Query(string id)
{
    string queryUrl = $"Query?id={id.ToString()}";
    int status = 100;
    string results = "";
    JObject returnedData = null;
    while (status == 100 || status == 123)
    {
        Task.Delay(500).Wait();
        var result = client.PostAsync(queryUrl, null).Result;
        var returnedContent = result.Content.ReadAsStringAsync().Result;
        returnedData = JObject.Parse(returnedContent);
        status = (int)returnedData.SelectToken("FileStatus");
    }

    if (status == 200)
        results = returnedData.SelectToken("RequestData").ToString();

    return results;
}

Add the downloadFile method for downloading the file.

void downloadFile(object sender, EventArgs args)
{
    Device.OpenUri(new Uri(fileUrl));
}

Conclusion

These two technologies provide developers with one, a high-level API to access a mobile devices camera, and two, a programmer-friendly Web API. Combining these gives developers the ability to create on the go mobile service applications.

Create an account and get your first 50 pages for free!

https://services.leadtools.com/account/register

Support

Need help getting this sample up and going? Contact our support team for free technical support! For pricing or licensing questions, you can contact our sales team (sales@leadtools.com) or call us at 704-332-5532.

LEADTOOLS Blog

LEADTOOLS Powered by Apryse,the Market Leading PDF SDK,All Rights Reserved