1. A brief introduction

Angular 10 became generally available as a production release on June 24, 2020.
The major upgrade to the Google-developed, TypeScript-based framework puts more emphasis on quality, tool, and ecosystem
improvements than new features.

It may not appear as impactful as the version 9 but embarks some notable changes:

  • New Date Range Picker
  • Warnings about CommonJS imports
  • Optional Stricter Settings
  • Keeping Up to Date with the Ecosystem
  • New Default Browser Configuration
  • Angular Team Fixit
  • Deprecations and Removals

At the end of this tutorial, you will be able to run an Angular 10 application on Visual Studio with DocuVieware.

This tutorial includes a clean-up panel, which is not necessary for the application to work. But it will show how to
implement a DocuVieware custom feature.

1.1. Set up your Visual Studio project

As a prerequisite, of course, you need Virtual Studio updated and the latest version of DocuVieware.
For this tutorial, we used Microsoft Visual Studio Enterprise 2019 version 16.6.4.

1.2. Set up the Angular 10 application

As a prerequisite, you need Node.js to be installed and set up properly in the PATH environment variable. You can verify
it by writing “node -v” into the command prompt. For this tutorial, we used Node.js 12.18.2.
Check also your Angular version by typing “ng –v”, and in case you have the old version, update it with this command:
→ ng update @angular/cli @angular/core

Illustration Angular10 update screenshot

2. Create your Visual Studio project

→ Launch Visual Studio
→ Create a new project

Create a new project Visual Studio screenshot

→ Create an ASP.NET Core Web Application

ASP.NET Core Web Application screenshot

→ Give a name to your project
→ Choose “Angular”

Choose “Angular”

Your project is now created, this is what you should see after running it:

Project Angular/DocuVieware ready screenshot

Delete all files and references related to the VS project template. They will be useless for the rest of this tutorial.

Files:
→ WeatherForecast.cs
→ Controllers/WeatherForecastController.cs
→ Folder ClientApp

3. Create your Angular 10 application

At the moment, Visual Studio does not create an Angular 10 app automatically. So we need to create the client app
manually with the command prompt in our VS project.

Warning: close your Visual Studio for creating properly the Angular 10 application inside. If you don’t
do this, Visual Studio will create the node modules dependencies and it will generate tons of errors.

→ In a command prompt go to your VS Project folder
→ ng new ClientApp
→ Yes
→ CSS

Open Visual Studio, you can see there is no “node_modules” folder, Visual Studio linked it to the dependencies itself.

Your Angular 10 app is now created into your VS project, but there are some errors. It is normal, at the moment, Visual
Studio is not configured to accept an Angular 10 native app.
For linking properly the Angular app to your project you need to modify the JSON files.

Copy the tsconfig.base.json (into ClientApp/tsconfig.json) and paste it into the tsconfig.json
Into package.json, change the <“start”: “ng serve”> parameter under <“script” to: "start": "echo hello && ng
serve">
.

This little piece of JSON is really important and will block your application if you don’t modify it.

By doing this, everything should work fine, so launch the application!

Angular 10 App screenshot

4. Add DocuVieware

Now, let’s take a look at how to add your DocuVieware SDK into the project.
For this, right-click on “dependencies” and select “Add project reference”, then, in the “browse” section, select “
GdPicture.NET.14.WEB.DocuVieware.Core.dll”. You will find it at C:\GdPicture.NET 14\Redist\DocuVieware (.NET Core 3.0)).
Our SDK is now well implemented to your solution, you should be able to get functionalities by adding an assembly: using
GdPicture14.WEB;

GdPicture14.WEB screenshot

Now create a new folder called “assets” in ClientApp/src/app/ and add docuvieware.min.js and docuvieware.min.css. You
can find these files in the path C:\GdPicture.NET 14\Redist\DocuVieware (Resources).

Angular.json

To link the app and DocuVieware js/css you need to refer the angular.json like this:

"styles": [
"src/styles.css",
"src/app/assets/docuvieware-min.css"
],
"scripts": [
"src/app/assets/docuvieware-min.js"
]

5. Code!

Now, let’s code.
But before, just for verification, at the end of this part, you should have this tree:

DocuVieware Angular 10 Code Tree screenshot

5.1 Controllers

Controllers are in charge of controlling the application by getting the input. It transfers order to the view from the
model and orders from the view to the model.

Create a folder “Controllers” at the root of your application.
→ Right-click on projects
→ “Add”
→ “New folder”
→ Name it “Controllers”

DocuVieware3.cs

Then, add the class.
→ Right-click on the created folder (Controllers)
→ Click “Add”, then “New Item..”
→ Choose “API Controller Class – Empty”
→ Name your file “DocuVieware3.cs”

This file is used to implement the HTTP communication method.

namespace WebApp.Controllers
{
 [ApiController]
 [Route("api/DocuVieware3")]
 public class DocuVieware3 : ControllerBase
 {
    // http get example
   [HttpGet("ping")]
   public string ping()
   {
     return "pong";
  }
} 
}

DocuViewareRESTController.cs

The file DocuViewareRESTController is where the controls are. In this file, you can choose to enable/disable and change
some parameters like the maximum upload size.
You can find them in our documentation.

→ Right-click on the created folder (Controllers)
→ Click “Add”, then “New Item..”
→ Choose “API Controller Class – Empty”
→ Name your file “DocuViewareRESTController.cs”

  namespace WebApp.Controllers
  {
     [ApiController]
     [Route("api/DocuVieware")]
     public class DocuViewareController : ControllerBase
     {
       [HttpPost("[action]")]
       public ActionResult GetDocuViewareControl(DocuViewareConfiguration controlConfiguration)
       {
         if (!DocuViewareManager.IsSessionAlive(controlConfiguration.SessionId))
         {
           if (!string.IsNullOrEmpty(controlConfiguration.SessionId)
  && !string.IsNullOrEmpty(controlConfiguration.ControlId))
           {
             DocuViewareManager.CreateDocuViewareSession(controlConfiguration.SessionId, controlConfiguration.ControlId, 20);
           }
           else
           {
             throw new Exception("Invalid session identifier and/or invalid control identifier.");
           }
         }
  
         using DocuViewareControl docuVieware = new DocuViewareControl(controlConfiguration.SessionId)
         {
           AllowPrint = controlConfiguration.AllowPrint,
            EnablePrintButton = controlConfiguration.EnablePrintButton,
            AllowUpload = controlConfiguration.AllowUpload,
            EnableFileUploadButton = controlConfiguration.EnableFileUploadButton,
            CollapsedSnapIn = controlConfiguration.CollapsedSnapIn,
            ShowAnnotationsSnapIn = controlConfiguration.ShowAnnotationsSnapIn,
            EnableRotateButtons = controlConfiguration.EnableRotateButtons,
            EnableZoomButtons = controlConfiguration.EnableZoomButtons,
            EnablePageViewButtons = controlConfiguration.EnablePageViewButtons,
            EnableMultipleThumbnailSelection = controlConfiguration.EnableMultipleThumbnailSelection,
            EnableMouseModeButtons = controlConfiguration.EnableMouseModeButtons,
            EnableFormFieldsEdition = controlConfiguration.EnableFormFieldsEdition,
            EnableTwainAcquisitionButton = controlConfiguration.EnableTwainAcquisitionButton,
            MaxUploadSize = 36700160
         };
  
         using StringWriter controlOutput = new StringWriter();
         docuVieware.RenderControl(controlOutput);
  
         return Ok(new DocuViewareResponse
         {
           HtmlContent = controlOutput.ToString()
         });
       }
     }
  }
  

5.2 Models

Models are in charge of providing the data to the application and store it too. But I don’t think there is an explicit
link with the getter and setter.

→ Right-click on projects
→ Add
→ New folder
→ Name it “Models”

DocuViewareConfiguration.cs

This file is used to define the getters and setters of our application.

Add the class.
→ Right-click on the created folder (Models)
→ Click “Add”, then “New Item..”
→ Choose “API Controller Class – Empty”
→ Name your file “DocuViewareConfiguration.cs”

namespace WebApp.Models
{
 public class DocuViewareConfiguration
 {
   public string SessionId { get; set; }
   public string ControlId { get; set; }
   public bool AllowPrint { get; set; }
   public bool EnablePrintButton { get; set; }
   public bool AllowUpload { get; set; }
   public bool EnableFileUploadButton { get; set; }
   public bool CollapsedSnapIn { get; set; }
   public bool ShowAnnotationsSnapIn { get; set; }
   public bool EnableRotateButtons { get; set; }
   public bool EnableZoomButtons { get; set; }
   public bool EnablePageViewButtons { get; set; }
   public bool EnableMultipleThumbnailSelection { get; set; }
   public bool EnableMouseModeButtons { get; set; }
   public bool EnableFormFieldsEdition { get; set; }
   public bool EnableTwainAcquisitionButton { get; set; }
  }
}

DocuViewareResponse.cs

Add the class.
→ Right-click on the created folder (Models)
→ Click “Add”, then “New Item..”
→ Choose “API Controller Class – Empty”
→ Name your file “DocuViewareResponse.cs”

This file is used to define the auto-properties of the HTML content. The client will ask the server what it wants, the
server will load web content and will send it back to the client.

DocuViewareResponse.cs

namespace DVClient.Models
{
 public class DocuViewareResponse
 {
   public string HtmlContent { get; set; }
 }
}

5.3 Docuvieware functionalities

The clean-up panel part will implement classes related to the DocuVieware custom features. For this tutorial, we will
implement the image quality improvement.

5.3.1 Clean-up Panel

The clean-up panel of DocuVieware is a functionality adding a panel of multiple tools to the application for improving
the quality of the document.

In a command prompt:
→ Go to DVClient/ClientApp/src/app/
→ ng g c cleanup-panel

cleanup-panel.component.ts

import { Component, OnInit } from '@angular/core';

declare var DocuViewareAPI: any;

@Component({
 selector: 'app-cleanup-panel',
 templateUrl: './cleanup-panel.component.html',
 styleUrls: ['./cleanup-panel.component.css']
})
export class CleanupPanelComponent implements OnInit {

DOCUVIEWARE_CONTROL_ID = 'DocuVieware1';

constructor() {
}

ngOnInit() {
}

applyFilter(customActionName: string) {
 const pages = DocuViewareAPI.GetSelectedThumbnailItems(this.DOCUVIEWARE_CONTROL_ID);
 const roi = DocuViewareAPI.GetSelectionAreaCoordinates(this.DOCUVIEWARE_CONTROL_ID);
 if (pages.length === 0) {
   pages[0] = DocuViewareAPI.GetCurrentPage(this.DOCUVIEWARE_CONTROL_ID);
 }
const param = { Pages: pages, RegionOfInterest: roi };

 DocuViewareAPI.PostCustomServerAction(this.DOCUVIEWARE_CONTROL_ID, true, customActionName, param);
}
}

cleanup-panel.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { CleanupPanelComponent } from './cleanup-panel.component';

describe('CleanupPanelComponent', () => {
 let component: CleanupPanelComponent;
 let fixture: ComponentFixture;

 beforeEach(async(() => {
   TestBed.configureTestingModule({
     declarations: [CleanupPanelComponent]
   })
   .compileComponents();
}));

 beforeEach(() => {
   fixture = TestBed.createComponent(CleanupPanelComponent);
   component = fixture.componentInstance;
  fixture.detectChanges();
 });

 it('should create', () => {
   expect(component).toBeTruthy();
 });
});

cleanup-panel.component.html

The HTML structure of our panel, you can copy it from the sample folder (provide the folder path)

cleanup-panel.component.css

In the CSS design of our panel, you can copy it from the sample folder (provide the folder path)

5.3.2 Image Cleanup

→ Create a folder at the project level and name it “DocuViewareDemo
→ Right-click on the “DocuViewareDemo” folder
→ Click “Add”, then “Class”
→ Name your file “ImageCleanupDemo.cs”

This code contains the image quality improvement methods of our file. It will implement the clean-up panel by using
“e.actionName”, the application gets the user’s action and chooses the right clean-up action according to that.

ImageCleanupDemo.cs

switch (e.actionName)
{
case "automaticRemoveBlackBorders":
status = gdPictureImaging.DeleteBlackBorders(imageId, 10, false);
break;
case "autoDeskew":
status = gdPictureImaging.AutoDeskew(imageId);
break;
case "punchHoleRemoval":
status = gdPictureImaging.RemoveHolePunch(imageId, HolePunchMargins.MarginLeft |     HolePunchMargins.MarginRight | HolePunchMargins.MarginBottom | HolePunchMargins.MarginTop);
break;
case "negative":
status = gdPictureImaging.FxNegative(imageId);
break;
case "despeckle":
status = gdPictureImaging.FxDespeckle(imageId);
break;
case "rotate-90":
status = gdPictureImaging.RotateAngle(imageId, 270);
break;
case "rotate+90":
status = gdPictureImaging.RotateAngle(imageId, 90);
break;
}

5.4 Docuvieware

The DocuVieware folder contains the style and code which will run our solution on the client-side and the integration
part of it.
To create the folder and files inside:

→ In a command prompt, go to ClientApp/src/app folder
→ ng g c docuvieware

-docuvieware.component.css → Where you define the DocuVieware look you need.
-docuvieware.component.html → The HTML which will be injected into the main HTML.

<div id="dvContainer" style="width:100%; height:100%;"></div>

docuvieware.component.spec.ts → Typescript spec code
docuvieware.component.ts → the code for injecting the HTML properly into the “dvContainer” div.

import { Component, OnInit } from '@angular/core';
import { DocuviewareClientApiService } from '../services/docuvieware-client-api.service';

@Component({
 selector: 'app-docuvieware',
 templateUrl: './docuvieware.component.html',
 styleUrls: ['./docuvieware.component.css']
})
export class DocuviewareComponent implements OnInit {

constructor(private dvApi: DocuviewareClientApiService) {
}
htmlMarkup: any;

private static insertInDOM(content: string): void {
 const fragment = document.createRange().createContextualFragment(content);
 document.getElementById('dvContainer').appendChild(fragment);
 }
ngOnInit() {
 this.dvApi.getDocuViewareMarkup().subscribe(
 response => DocuviewareComponent.insertInDOM(response.htmlContent),
 error => this.htmlMarkup = error as any
 );
 }
}

5.5 Application models

The model folder defines the REST service structure:

→ In a command prompt, go to DVClient/ClientApp/src/app/
→ ng g m models
→ rename the created file to “docuvieware-response.ts”

Docuvieware-response.ts

This file defines the structure of the HTML content of the server response.

export class DocuviewareResponse {
htmlContent: string;
}

5.6. Services folder

The model folder contains the REST layer to communicate with the server properly.

→ In a command prompt, go to DVClient/ClientApp/src/app/
→ ng g s services/docuvieware-client-api

docuvieware.service.ts

The code providing the REST communication with the API URL and all the controls configuration to activate/deactivate
functionalities into your app.
This is where you can choose if you want to display the controls in your application.
You will find all properties in our online documentation.

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { DocuviewareResponse } from '../models/docuvieware-response';


@Injectable({
 providedIn: 'root'
})
export class DocuviewareClientApiService {
 DOCUVIEWARE_CONTROL_ID = 'DocuVieware1';
 DOCUVIEWARE_ENDPOINT_BASE_URL = 'http://localhost:5000/api/DocuVieware';
 DOCUVIEWARE_GETMARKUP_ENDPOINT = 'GetDocuViewareControl';

constructor(private httpClient: HttpClient) {
}

getDocuViewareMarkup(): Observable {
 const httpOptions = {
 headers: new HttpHeaders({
 'Content-Type': 'application/json'
 })
};

const docuViewareConfig = {
 SessionId: 'mySessionId', // Set to an arbitrary value, should be replaced by the session identifier from your session mechanism
 ControlId: this.DOCUVIEWARE_CONTROL_ID,
 AllowPrint: true,
 EnablePrintButton: true,
 AllowUpload: true,
 EnableFileUploadButton: true,
 CollapsedSnapIn: true,
 ShowAnnotationsSnapIn: true,
 EnableRotateButtons: true,
 EnableZoomButtons: true,
 EnablePageViewButtons: true,
 EnableMultipleThumbnailSelection: true,
 EnableMouseModeButtons: true,
 EnableFormFieldsEdition: true,
 EnableTwainAcquisitionButton: true,
};
return this.httpClient.post(`${this.DOCUVIEWARE_ENDPOINT_BASE_URL}/${this.DOCUVIEWARE_GETMARKUP_ENDPOINT}/`, docuViewareConfig, httpOptions);
}
}

5.7 Setting main files

→ Right-click on the project
→ Click “Add”, then “Class”
→ Name the file “Globals.cs”

Globals.cs

using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.IO;
using GdPicture14.WEB;
using WebApp.DocuViewareCoreDemo;

namespace WebApp
{
 public static class Globals
 {
 private static readonly string m_rootDirectory = Directory.GetCurrentDirectory();
 public static readonly int SESSION_TIMEOUT = 20; //Set to 20 minutes. use -1 to handle DocuVieware session timeout through asp.net session mechanism.
 public const bool STICKY_SESSION = true; //Set false to use DocuVieware on Servers Farm witn non sticky sessions.
 public const DocuViewareSessionStateMode DOCUVIEWARE_SESSION_STATE_MODE = DocuViewareSessionStateMode.File; //Set DocuViewareSessionStateMode.File is STICKY_SESSION is False.


 public static string GetCacheDirectory()
 {
   return m_rootDirectory + "\\cache";
 }


 public static string GetDocumentsDirectory()
 {
   return m_rootDirectory + "\\documents";
 }


 public static string BuildDocuViewareControlSessionID(HttpContext HttpContext, string clientID)
 {
   if (HttpContext.Session.GetString("DocuViewareInit") == null)
   {
     HttpContext.Session.SetString("DocuViewareInit", "true");
   }

 return HttpContext.Session.Id + clientID;
 }


 public static DocuViewareLocale GetDocuViewareLocale(HttpRequest request)
 {
   if (request != null)
 {
 IList acceptLanguage = request.GetTypedHeaders().AcceptLanguage;

 if (acceptLanguage != null)
 {
   foreach (StringWithQualityHeaderValue language in acceptLanguage)
   {
   object docuviewareLocale;
   if (Enum.TryParse(typeof(DocuViewareLocale), language.Value.Value, true, out docuviewareLocale))
   {
     return (DocuViewareLocale)docuviewareLocale;
   }
 }
}
}

return DocuViewareLocale.En;
}


public static void CustomActionDispatcher(object sender, CustomActionEventArgs e)
{
 switch (e.actionName)
 {
 case "automaticRemoveBlackBorders":
 case "autoDeskew":
 case "punchHoleRemoval":
 case "negative":
 case "despeckle":
 case "rotate-90":
 case "rotate+90":
 ImageCleanupDemo.HandleImageCleanupAction(e);
 break;
 }
}
}
}

Startup.cs

using GdPicture14.WEB;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace WebApp
{
 public class Startup
 {
   public Startup(IConfiguration configuration)
   {
   Configuration = configuration;
   DocuViewareLicensing.RegisterKEY(Configuration["YOUR_API_KEY"]); //Unlocking DocuVieware. Please set your demo or commercial license key here or in config file.
   DocuViewareManager.SetupConfiguration(Globals.STICKY_SESSION, Globals.DOCUVIEWARE_SESSION_STATE_MODE, Globals.GetCacheDirectory(), "http://localhost:5000", "api/docuvieware3");
 DocuViewareEventsHandler.CustomAction += Globals.CustomActionDispatcher;
   }

   public IConfiguration Configuration { get; }

   // This method gets called by the runtime. Use this method to add services to the container.
   public void ConfigureServices(IServiceCollection services)
   {
     services.AddControllersWithViews();
     // In production, the Angular files will be served from this directory
     services.AddSpaStaticFiles(configuration =>
     {
       configuration.RootPath = "ClientApp/dist";
     });
   }

   // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
   public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger)
   {
     if (env.IsDevelopment())
   {
   app.UseDeveloperExceptionPage();
   }
   else
   {
     app.UseExceptionHandler("/Error");
   }
 app.UseCors(cors => cors.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowAnyMethod());

 app.UseStaticFiles();
 if (!env.IsDevelopment())
 {
   app.UseSpaStaticFiles();
 }

 app.UseRouting();

 app.UseEndpoints(endpoints =>
 {
   endpoints.MapControllerRoute(
   name: "default",
   pattern: "{controller}/{action=Index}/{id?}");
 });

 app.UseSpa(spa =>
 { 
   spa.Options.SourcePath = "ClientApp";

 if (env.IsDevelopment())
 {
   spa.UseAngularCliServer(npmScript: "start");
 }
});
}
}
}

Program.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using WebApp;

namespace DocuviewareAngular10Demo_proofreading
{
 public class Program
 {
 public static void Main(string[] args)
   {
   CreateHostBuilder(args).Build().Run();
   }

   public static IHostBuilder CreateHostBuilder(string[] args) =>
   Host.CreateDefaultBuilder(args)
   .ConfigureWebHostDefaults(webBuilder =>
   {
     webBuilder.UseStartup();
   });
 }
}

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

export function getBaseUrl() {
 return document.getElementsByTagName('base')[0].href;
}

const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];

if (environment.production) {
 enableProdMode();
}

platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.log(err));

app.component.html

In this file, we need to put the two different HTML parts we made, the DocuVieware interface and the cleanup panel.

<app-cleanup-panel></app-cleanup-panel>
<app-docuvieware style="width:100%; height:100%;"></app-docuvieware>

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { DocuviewareComponent } from './docuvieware/docuvieware.component';
import { CleanupPanelComponent } from './cleanup-panel/cleanup-panel.component';

@NgModule({
declarations: [
AppComponent,
DocuviewareComponent,
CleanupPanelComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

6. Enjoy your app!

Now, launch the app, and let’s see what happens!

DocuVieware Angular 10 application screenshot

Our DocuVieware Angular 10 application is well created with all the basic controls.
You can also add TWAIN communication and much more, check the documentation for more
info.

Let us know if you have any questions, and we’ll be happy to help!

 

Cheers,

Fabio