diff --git a/How to/Redact a PDF Document/Angular Client/.editorconfig b/How to/Redact a PDF Document/Angular Client/.editorconfig
new file mode 100644
index 0000000..59d9a3a
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/.editorconfig
@@ -0,0 +1,16 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/How to/Redact a PDF Document/Angular Client/.gitignore b/How to/Redact a PDF Document/Angular Client/.gitignore
new file mode 100644
index 0000000..0711527
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/.gitignore
@@ -0,0 +1,42 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# Compiled output
+/dist
+/tmp
+/out-tsc
+/bazel-out
+
+# Node
+/node_modules
+npm-debug.log
+yarn-error.log
+
+# IDEs and editors
+.idea/
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# Visual Studio Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# Miscellaneous
+/.angular/cache
+.sass-cache/
+/connect.lock
+/coverage
+/libpeerconnection.log
+testem.log
+/typings
+
+# System files
+.DS_Store
+Thumbs.db
diff --git a/How to/Redact a PDF Document/Angular Client/README.md b/How to/Redact a PDF Document/Angular Client/README.md
new file mode 100644
index 0000000..b2cbbe3
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/README.md
@@ -0,0 +1,27 @@
+# Getting started
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.4.
+
+## Development server
+
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
diff --git a/How to/Redact a PDF Document/Angular Client/angular.json b/How to/Redact a PDF Document/Angular Client/angular.json
new file mode 100644
index 0000000..419ea66
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/angular.json
@@ -0,0 +1,101 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "my-app": {
+ "projectType": "application",
+ "schematics": {},
+ "root": "",
+ "sourceRoot": "src",
+ "prefix": "app",
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "outputPath": "dist/my-app",
+ "index": "src/index.html",
+ "main": "src/main.ts",
+ "polyfills": [
+ "zone.js"
+ ],
+ "tsConfig": "tsconfig.app.json",
+ "assets": [
+ "src/favicon.ico",
+ "src/assets"
+ ],
+ "styles": [
+ "src/styles.css"
+ ],
+ "scripts": []
+ },
+ "configurations": {
+ "production": {
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "500kb",
+ "maximumError": "1mb"
+ },
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "2kb",
+ "maximumError": "4kb"
+ }
+ ],
+ "outputHashing": "all"
+ },
+ "development": {
+ "buildOptimizer": false,
+ "optimization": false,
+ "vendorChunk": true,
+ "extractLicenses": false,
+ "sourceMap": true,
+ "namedChunks": true
+ }
+ },
+ "defaultConfiguration": "production"
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "configurations": {
+ "production": {
+ "browserTarget": "my-app:build:production"
+ },
+ "development": {
+ "browserTarget": "my-app:build:development"
+ }
+ },
+ "defaultConfiguration": "development"
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "my-app:build"
+ }
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "polyfills": [
+ "zone.js",
+ "zone.js/testing"
+ ],
+ "tsConfig": "tsconfig.spec.json",
+ "assets": [
+ "src/favicon.ico",
+ "src/assets"
+ ],
+ "styles": [
+ "src/styles.css"
+ ],
+ "scripts": []
+ }
+ }
+ }
+ }
+ },
+ "cli": {
+ "analytics": "8d9c65e2-01c9-4a66-bc68-685c535cc647"
+ }
+}
diff --git a/How to/Redact a PDF Document/Angular Client/package.json b/How to/Redact a PDF Document/Angular Client/package.json
new file mode 100644
index 0000000..c1b3e9b
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "my-app",
+ "version": "0.0.0",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "build": "ng build",
+ "watch": "ng build --watch --configuration development",
+ "test": "ng test"
+ },
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "^15.0.0",
+ "@angular/common": "^15.0.0",
+ "@angular/compiler": "^15.0.0",
+ "@angular/core": "^15.0.0",
+ "@angular/forms": "^15.0.0",
+ "@angular/platform-browser": "^15.0.0",
+ "@angular/platform-browser-dynamic": "^15.0.0",
+ "@angular/router": "^15.0.0",
+ "@syncfusion/ej2-angular-pdfviewer": "*",
+ "@syncfusion/ej2-angular-navigations": "*",
+ "@syncfusion/ej2-angular-popups":"*",
+ "@syncfusion/ej2-angular-buttons":"*",
+ "@syncfusion/ej2-angular-dropdowns":"*",
+ "@syncfusion/ej2-angular-inputs":"*",
+ "rxjs": "~7.5.0",
+ "tslib": "^2.3.0",
+ "zone.js": "~0.12.0"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "^15.0.4",
+ "@angular/cli": "~15.0.4",
+ "@angular/compiler-cli": "^15.0.0",
+ "@types/jasmine": "~4.3.0",
+ "jasmine-core": "~4.5.0",
+ "karma": "~6.4.0",
+ "karma-chrome-launcher": "~3.1.0",
+ "karma-coverage": "~2.2.0",
+ "karma-jasmine": "~5.1.0",
+ "karma-jasmine-html-reporter": "~2.0.0",
+ "typescript": "~4.8.2"
+ }
+}
diff --git a/How to/Redact a PDF Document/Angular Client/src/app/app.component.css b/How to/Redact a PDF Document/Angular Client/src/app/app.component.css
new file mode 100644
index 0000000..6dca142
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/app/app.component.css
@@ -0,0 +1,391 @@
+/* custom code start*/
+#magnificationToolbar {
+ background: transparent;
+ height: auto;
+ min-height: 56px;
+ width: 200px;
+ border: none;
+ position: absolute;
+ z-index: 1001;
+ bottom: 58px;
+ right: 16px;
+ transform: rotate(90deg);
+}
+
+.e-pv-fit-page-icon::before {
+ content: '\e91b';
+ font-family: "e-icons";
+}
+
+#magnificationToolbar.e-toolbar .e-toolbar-items {
+ background: transparent;
+}
+
+#magnificationToolbar.e-toolbar .e-tbar-btn {
+ border-radius: 50%;
+ min-height: 30px;
+ min-width: 30px;
+ border: 1px solid #c8c8c8;
+}
+
+#topToolbar {
+ top: 0px;
+ z-index: 1001;
+}
+
+.e-tbar-section .e-sample-resize-container {
+ height: 46px;
+}
+
+.e-bookmark-popup {
+ height: 200px;
+ max-width: 250px;
+}
+
+.e-text-search-popup {
+ height: 104px;
+ max-width: 348px;
+}
+
+.e-custom-search-input {
+ width: 234px;
+}
+
+.e-text-search-popup .e-footer-content, .e-bookmark-popup .e-footer-content {
+ padding: 0;
+ height: 0;
+}
+
+.search-button, .search-button:disabled, .search-button:focus, .search-button:hover {
+ background: transparent;
+ box-shadow: none;
+}
+
+#popup .e-dlg-content {
+ padding-left: 0;
+ padding-bottom: 0;
+}
+
+.e-pv-bookmarks {
+ min-width: 234px;
+}
+
+.e-pv-current-page-number {
+ width: 46px;
+ height: 28px;
+ text-align: center;
+}
+
+.material .e-pv-current-page-number {
+ border-width: 1px;
+}
+.e-pv-zoom-out-sample {
+ transform: rotate(90deg);
+ padding-right: 2px;
+}
+.customtoolbar.e-toolbar {
+ background-color: #3f51b5;
+width: 50px;
+}
+.customtoolbar.e-toolbar .e-toolbar-items{
+ background-color: #3f51b5;
+ height: 40px;
+ display: flex;
+ flex-wrap: wrap;
+
+}
+.e-pv-icon-search::before {
+ font-size: 12px;
+}
+.e-pv-search-input.e-input-group .e-input-search-group-icon.e-input-group-icon {
+ background: rgba(0,0,0,0);
+}
+
+/* custom code end*/
+
+/* style for slider button*/
+
+.flex-container {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.render-mode-info {
+ background: none;
+ border: none;
+ padding-left: 0px;
+}
+
+.render-mode-info .render-mode-info-icon {
+ height: 16px;
+ width: 16px;
+}
+
+.switchLabel {
+ font-family: "Segoe UI", "GeezaPro", "DejaVu Serif", sans-serif;
+ font-weight: 400;
+ line-height: 20px;
+ letter-spacing: 0.24px;
+ text-align: right;
+ font-size: 14px;
+}
+
+.render-mode-info .render-mode-info-icon::before {
+ line-height: 0.5rem;
+}
+
+.buttonSwitch {
+ Width: 40px;
+ Height: 24px;
+}
+
+.pdfviewer-property-section{
+ padding: 20px 15px;
+}
+
+.pdfviewer-contextmenu-checkbox-label{
+ padding: 10px;
+}
+
+.pdfviewer-control-section {
+ border-right: 1px solid #D7D7D7;
+}
+
+.pdfviewer-property-container{
+ padding-bottom: 30px;
+}
+
+.header-pdfviewer {
+ font-weight: 600;
+ padding: 20px 2px;
+}
+/*redaction style*/
+#e-pv-redact-sb-panel.control-section .e-appbar {
+ height: 36px;
+ font-size: 14px !important;
+}
+
+#e-pv-redact-sb-toolbar.e-toolbar .e-toolbar-item .e-tbar-btn {
+ flex-direction: column;
+}
+#e-pv-redact-sb-toolbar.e-toolbar .e-toolbar-item .e-tbar-btn .e-icons.e-btn-icon
+{
+ font-size: 18px;
+}
+#e-pv-redact-sb-toolbar
+{
+ padding: 0 9px;
+}
+#e-pv-redact-sb-toolbar .e-toolbar-items
+{
+ gap: 5px;
+}
+
+#e-pv-redact-sb-toolbar .e-toolbar-item.e-separator
+{
+ height: 48px !important;
+}
+#e-pv-redact-sb-toolbar , .e-toolbar
+{
+ border-left-width: 1px !important;
+ border-right-width: 1px !important;
+}
+
+#e-pv-redact-sb-toolbar-secondary .e-toolbar
+{
+ border-top-width: 1px !important;
+ border: 0 solid var(--color-sf-border-light);
+ --color-sf-border-light: #e0e0e0;
+}
+
+#e-pv-redact-sb-currentPage
+{
+ font-size: 12px;
+}
+#e-pv-redact-sb-totalPage
+{
+ font-weight: 400;
+}
+
+.e-pv-redact-sb-black-out-container .black-out {
+ background-color: black;
+ width: 18px !important;
+ height: 18px !important;
+ border: 1px solid white;
+ border-radius: 3px;
+ margin:4px !important;
+ }
+.e-pv-redact-sb-white-out-container .white-out {
+ background-color: white;
+ width: 18px !important;
+ height: 18px !important;
+ border: 1px solid black;
+ border-radius: 3px;
+ margin:4px !important;
+}
+.e-bigger .e-pv-redact-sb-black-out-container .black-out,
+ .e-bigger .e-pv-redact-sb-white-out-container.white-out {
+ position: relative;
+ top: -4px;
+}
+.e-bigger .e-toolbar .e-toolbar-item.e-pv-redact-sb-black-out-container .e-tbar-btn.e-btn,
+ .e-bigger .e-toolbar .e-toolbar-item.e-pv-redact-sb-white-out-container .e-tbar-btn.e-btn {
+ padding-top : 5px !important;
+}
+.e-bigger .e-pv-redact-sb-black-out-container .e-tbar-btn .e-tbar-btn-text,
+ .e-bigger .e-pv-redact-sb-white-out-container .e-tbar-btn .e-tbar-btn-text{
+ padding: 2px 4px 7px 4px !important;
+}
+/*upload image*/
+#e-pv-redact-sb-dialog.e-dialog #e-pv-redact-sb-defaultfileupload.e-upload-files {
+ border: none !important;
+ }
+#e-pv-redact-sb-panel.control-section #file-uploader
+{
+ display: flex;
+ border: 1px dashed #c8c6c4;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 12px;
+ padding: 16px;
+ border-radius: 6px;
+}
+#e-pv-redact-sb-dialog.e-dialog .e-dlg-header-content
+{
+ border-bottom:1px solid #C4C7C5 !important;
+ padding: 15px !important;
+}
+
+#e-pv-redact-sb-dialog.e-dialog .e-footer-content
+{
+ border-top:1px solid #C4C7C5 !important;
+ padding: 10px !important;
+}
+
+#e-pv-redact-sb-dialog.e-dialog .e-dlg-header-content+.e-dlg-content
+{
+ padding: 20px !important;
+}
+
+#e-pv-redact-sb-panel.control-section .e-file-select-wrap
+{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#e-pv-redact-sb-dialog.e-dialog .e-footer-content .e-btn
+{
+ background: rgba(0, 0, 0, 0);
+ border-color: rgba(0, 0, 0, 0);
+ box-shadow: none;
+ color: #242424;
+}
+
+#e-pv-redact-sb-drop-area-wrap .e-upload-file-list {
+ display: none;
+}
+#e-pv-redact-sb-defaultfileupload .e-upload-files {
+ border: none !important;
+}
+.e-pv-redact-sb-image-source {
+ width: 70px;
+ height: 70px;
+}
+.e-pv-redact-sb-image-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 4px;
+ gap: 5px;
+ border-radius: 4px;
+ position: relative;
+}
+
+.e-pv-redact-sb-image-list {
+ display: flex;
+ justify-content: center;
+ padding: 24px 15px;
+ gap: 35px;
+}
+
+.e-pv-redact-sb-image-container-selected {
+ border: 1px solid #757575 !important;
+}
+
+#e-pv-redact-sb-drop-area-wrap {
+ display: flex;
+ border: 1px dashed #c8c6c4;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 12px;
+ padding: 16px;
+ border-radius: 6px;
+}
+
+#e-pv-redact-sb-defaultfileupload .e-upload {
+ border: none;
+ margin-top: 10px;
+ width: 100%;
+ height:100%;
+}
+
+#e-pv-redact-sb-panel .e-dlg-container
+{
+ --color-sf-overlay-bg-color: none !important;
+}
+
+.e-icons.e-redaction:before
+{
+ content:"\e740";
+}
+
+#e-pv-redact-sb-defaultfileupload .e-file-drop
+{
+ position: relative;
+ left: 9rem;
+ bottom: 17px;
+ color: #616161;
+}
+
+#e-pv-redact-sb-panel.control-section .e-dlg-overlay
+{
+ background: none;
+ backdrop-filter: blur(2px) !important;
+}
+
+#e-pv-redact-sb-dialog
+{
+ top: -152px;
+}
+
+#e-pv-redact-sb-panel .control-section
+{
+ min-height: 0px !important;
+}
+
+#e-pv-redact-sb-toolbar-secondary .e-input-group {
+ border: none !important;
+}
+
+#e-pv-redact-sb-toolbar #e-pv-redact-sb-toolbar_popup
+{
+ max-height: 500px !important;
+}
+
+#e-pv-redact-sb-toolbar #e-pv-redact-sb-toolbar_popup .e-toolbar-item
+{
+ min-height: 70px !important;
+}
+
+#e-pv-redact-sb-toolbar-secondary .e-toolbar-items .e-template
+{
+ min-width: 0px !important;
+}
+
+#e-pv-redact-sb-dialog
+{
+ max-height: 600px !important;
+}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Angular Client/src/app/app.component.html b/How to/Redact a PDF Document/Angular Client/src/app/app.component.html
new file mode 100644
index 0000000..0f7c694
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/app/app.component.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+ Redaction
+
+ Download
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+ Upload Image
+
+
+
+
+
+
+
Or drop files here
+
(Only JPG and PNG images will be accepted)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Angular Client/src/app/app.component.spec.ts b/How to/Redact a PDF Document/Angular Client/src/app/app.component.spec.ts
new file mode 100644
index 0000000..03e0fd7
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/app/app.component.spec.ts
@@ -0,0 +1,31 @@
+import { TestBed } from '@angular/core/testing';
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [
+ AppComponent
+ ],
+ }).compileComponents();
+ });
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'my-app'`, () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app.title).toEqual('my-app');
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement as HTMLElement;
+ expect(compiled.querySelector('.content span')?.textContent).toContain('my-app app is running!');
+ });
+});
diff --git a/How to/Redact a PDF Document/Angular Client/src/app/app.component.ts b/How to/Redact a PDF Document/Angular Client/src/app/app.component.ts
new file mode 100644
index 0000000..7127fa6
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/app/app.component.ts
@@ -0,0 +1,437 @@
+import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
+import { PdfViewerComponent, LinkAnnotationService, BookmarkViewService, MagnificationService, ToolbarService, NavigationService, TextSelectionService, PrintService, PageChangeEventArgs, LoadEventArgs, AnnotationService, FormDesignerService, PageOrganizerService, TextSearchService, PdfViewerModule, AnnotationAddEventArgs, AnnotationRemoveEventArgs, CreateArgs, MouseEventArgs } from '@syncfusion/ej2-angular-pdfviewer';
+import { ToolbarComponent, ToolbarModule, MenuModule, AppBarModule, ChangeEventArgs } from '@syncfusion/ej2-angular-navigations';
+import { DialogComponent, DialogModule } from '@syncfusion/ej2-angular-popups';
+import { ClickEventArgs } from '@syncfusion/ej2-buttons';
+import { ButtonComponent, ButtonModule, SwitchModule } from '@syncfusion/ej2-angular-buttons';
+
+
+import { ComboBoxModule } from '@syncfusion/ej2-angular-dropdowns';
+import { UploaderComponent, UploaderModule } from '@syncfusion/ej2-angular-inputs';
+/**
+ * Default PdfViewer Controller
+ */
+@Component({
+ selector: 'app-root',
+ templateUrl: 'app.component.html',
+ encapsulation: ViewEncapsulation.None,
+ providers: [LinkAnnotationService, BookmarkViewService, TextSearchService, TextSelectionService, MagnificationService, ToolbarService, NavigationService, TextSelectionService, PrintService, AnnotationService, FormDesignerService, PageOrganizerService],
+ styleUrls: ['app.component.css'],
+ standalone: true,
+ imports: [
+ SwitchModule,
+ AppBarModule,
+ ToolbarModule,
+ MenuModule,
+ PdfViewerModule,
+
+
+ ButtonModule,
+ DialogModule,
+ UploaderModule,
+ ComboBoxModule
+ ],
+})
+
+export class AppComponent {
+ title(title: any) {
+ throw new Error('Method not implemented.');
+ }
+
+ @ViewChild('pdfviewer')
+ public pdfviewerControl: PdfViewerComponent | undefined;
+
+ @ViewChild('primaryToolbar')
+ public primaryToolbar: ToolbarComponent | undefined;
+
+ @ViewChild('secondaryToolbar')
+ public secondaryToolbar: ToolbarComponent | undefined;
+
+ @ViewChild('defaultButtonDownload') downloadBtn: ButtonComponent | undefined;
+
+ @ViewChild('dialogComponent')
+ public dialog: DialogComponent | undefined;
+
+ @ViewChild('cancelButton')
+ public cancelButton: ButtonComponent | undefined;
+ public showCloseIcon: Boolean = true;
+ public height = '482px';
+ public target = '#e-pv-redact-sb-panel';
+ public width = '477px';
+ public visible: Boolean = false;
+ public isModal: Boolean = true;
+
+ @ViewChild('defaultupload')
+ public uploadObj: UploaderComponent | undefined;
+ public path: Object = {
+ saveUrl: 'https://services.syncfusion.com/angular/production/api/FileUploader/Save',
+ removeUrl: 'https://services.syncfusion.com/angular/production/api/FileUploader/Remove'
+ };
+ public dropElement: HTMLElement = document.getElementsByClassName('drop-area-wrap')[0] as HTMLElement;
+ public allowedExtensions = '.png, .jpg, .jpeg';
+ public imageSrc: any;
+
+ public onFileChange(args: any): void {
+ var file = args.file[0].rawFile;
+ let imageElement = document.getElementById('imageView');
+ let imageElementContainer = document.getElementById('imageContainer');
+ let base64String: any;
+ var reader = new FileReader();
+ reader.onload = (e) => {
+ base64String = e.target?.result as string;
+ this.imageSrc = base64String;
+ this.customStampSource = this.imageSrc;
+ (imageElement as any).src = this.imageSrc;
+ if(imageElementContainer)
+ imageElementContainer.className =
+ 'image-container e-pv-redact-sb-image-container-selected';
+ (imageElement as any).style.display = 'block';
+ // Bind click event to the image element
+ if(imageElement)
+ imageElement.addEventListener('click', this.handleImageClick.bind(this));
+ };
+ reader.readAsDataURL(file);
+ }
+
+ public handleImageClick() {
+ this.customStampSource = this.imageSrc;
+ if(this.dialog)
+ this.dialog.hide();
+ this.addImage();
+ }
+
+ public ngAfterViewInit(): void {
+ if(this.downloadBtn)
+ this.downloadBtn.element.setAttribute("aria-label", "menu");
+ }
+
+ public document: string = "https://cdn.syncfusion.com/content/pdf/programmatical-annotations.pdf";
+ public resource: string = "https://cdn.syncfusion.com/ej2/27.2.2/dist/ej2-pdfviewer-lib";
+ public url: string = "https://ej2services.syncfusion.com/angular/development/api/pdfviewer/Redaction";
+
+ //zoom value
+ public data: string[] = ['10%', '25%', '50%', '75%', '100%', '200%', '400%'];
+ ngOnInit(): void {
+ const fileUploadElement = document.getElementById('fileUpload');
+ if(fileUploadElement) {
+ fileUploadElement.addEventListener('change', this.readFile.bind(this));
+ }
+ }
+
+ public annotation: any;
+ public redactionCount: number = 0;
+ public fileName: string = "programmatical-annotations.pdf";
+ //Updating the number of redaction while the annotation has been added
+ annotationAdd = (e: AnnotationAddEventArgs): void => {
+ var pdfAnnotationList = new Array();
+ if(this.pdfviewerControl)
+ pdfAnnotationList = this.pdfviewerControl.annotationCollection;
+ var selectedAnnotationIndex = pdfAnnotationList.findIndex(item => item.annotationId == e.annotationId);
+ if (selectedAnnotationIndex != -1) {
+ this.annotation = pdfAnnotationList[selectedAnnotationIndex];
+ }
+ if (this.annotation.author == "Redaction" || this.annotation.customStampName == "Image" || this.annotation.author == "Pattern" || this.annotation.author == "Text") {
+ this.redactionCount = this.redactionCount + 1;
+ this.updateRedaction();
+ }
+
+ }
+ //Updating the number of redaction while the annotation has been removed
+ annotationRemove = (e: AnnotationRemoveEventArgs): void => {
+ if (this.annotation.author == "Redaction" || this.annotation.customStampName == "Image" || this.annotation.author == "Pattern" || this.annotation.author == "Text") {
+ this.redactionCount = this.redactionCount - 1;
+ this.updateRedaction();
+ }
+ }
+
+ //To enable the redaction button based on count
+ public updateRedaction(): void {
+ if(this.primaryToolbar)
+ if (this.redactionCount <= 0) {
+ this.primaryToolbar.items[8].disabled = true;
+ }
+ else {
+ this.primaryToolbar.items[8].disabled = false;
+ }
+ }
+
+ //to read the file
+ // tslint:disable-next-line
+ private readFile(args: any): void {
+ // tslint:disable-next-line
+ let upoadedFiles: any = args.target.files;
+ if (args.target.files[0] !== null) {
+ let uploadedFile: File = upoadedFiles[0];
+ this.fileName = upoadedFiles[0].name;
+ if (uploadedFile) {
+ let reader: FileReader = new FileReader();
+ reader.readAsDataURL(uploadedFile);
+ // tslint:disable-next-line
+ let proxy: any = this;
+ // tslint:disable-next-line
+ reader.onload = (e: any): void => {
+ let uploadedFileUrl: string = e.currentTarget.result;
+ proxy.pdfviewerControl.documentPath = uploadedFileUrl;
+ proxy.pdfviewerControl.fileName = proxy.fileName;
+ proxy.pdfviewerControl.downloadFileName = proxy.fileName;
+ };
+ }
+ }
+ }
+
+ //To open a file from viewer
+ public openDocumentClicked(e: ClickEventArgs): void {
+ const fileUploadElement = document.getElementById('fileUpload');
+ if(fileUploadElement)
+ fileUploadElement.click();
+ }
+
+ //when the image button is clicked
+ public imageDialog = (): void => {
+ if(this.dialog)
+ this.dialog.show();
+ }
+
+ //when cancel button clicked
+ public closeDialog(e: MouseEvent): void {
+ if(this.dialog)
+ this.dialog.hide();
+ }
+
+ //Method to create rectangle annotation when "Text" button is clicked
+ public addText(e: ClickEventArgs): void {
+ if(this.pdfviewerControl)
+ this.pdfviewerControl.rectangleSettings = {
+ fillColor: '#a3a2a0',
+ strokeColor: '#a3a2a0',
+ author: 'Text'
+ }
+ if(this.pdfviewerControl)
+ this.pdfviewerControl.annotation.setAnnotationMode('Rectangle');
+ }
+
+ public customStampSource: any = "";
+ //Adding the image to the pdf
+ public addImage(): void {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.stampSettings.author = "Image";
+ this.pdfviewerControl.customStampSettings = {
+ width: 200,
+ author: 'Image',
+ height: 125,
+ isAddToMenu: false,
+ enableCustomStamp: false
+
+ };
+ this.pdfviewerControl.customStamp = [
+ {
+ customStampName: 'Image',
+ customStampImageSource: this.customStampSource
+ },
+ ];
+ }
+ }
+
+
+ //Method to create rectangle annotation when the "Pattern" button is clicked
+ public addPattern(e: ClickEventArgs): void {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.rectangleSettings = {
+ fillColor: '#dedfe0',
+ strokeColor: '#dedfe0',
+ author: 'Pattern'
+ }
+ this.pdfviewerControl.annotation.setAnnotationMode('Rectangle');
+ }
+ }
+
+ //Method to create rectangle annotation when the "Blackout" button is clicked
+ public addBlackout(e: ClickEventArgs): void {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.rectangleSettings = {
+ fillColor: '#000000',
+ strokeColor: '#000000',
+ author: 'Redaction'
+ }
+ this.pdfviewerControl.annotation.setAnnotationMode('Rectangle');
+ }
+ }
+
+ //Method to create rectangle annotation when the "Whiteout" button is clicked
+ public addWhiteout(e: ClickEventArgs): void {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.rectangleSettings = {
+ fillColor: '#ffffff',
+ strokeColor: '#ffffff',
+ author: 'Redaction'
+ }
+ this.pdfviewerControl.annotation.setAnnotationMode('Rectangle');
+
+ }
+ }
+ //To download the redacted pdf
+ public download(e: MouseEvent): void {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.saveAsBlob().then((blob) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onload = (e: ProgressEvent) => {
+ const base64String = e.target?.result;
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', this.url, true);
+ xhr.setRequestHeader('Content-type', 'application/json; charset=UTF-8');
+ const requestData = JSON.stringify({ base64String });
+ xhr.onload = () => {
+ if (xhr.status === 200) {
+ const responseBase64 = xhr.responseText.split('base64,')[1];
+ if (responseBase64) {
+ const blob = this.createBloburl(responseBase64, 'application/pdf');
+ const blobUrl = URL.createObjectURL(blob);
+ this.downloadDocument(blobUrl);
+ } else {
+ console.error('Invalid base64 response.');
+ }
+ } else {
+ console.error('Download failed:', xhr.statusText);
+ }
+ };
+ xhr.onerror = () => {
+ console.error('An error occurred during the download:', xhr.statusText);
+ };
+ xhr.send(requestData);
+ };
+ }).catch((error) => {
+ console.error('Error saving Blob:', error);
+ });
+ }
+ }
+
+ public createBloburl(base64String: string, contentType: string):Blob{
+ const sliceSize = 512;
+ const byteCharacters = atob(base64String);
+ const byteArrays: Uint8Array[] = [];
+ for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
+ const slice = byteCharacters.slice(offset, offset + sliceSize);
+ const byteNumbers = Array.from(slice, char => char.charCodeAt(0));
+ byteArrays.push(new Uint8Array(byteNumbers));
+ }
+ return new Blob(byteArrays, { type: contentType });
+ }
+
+ public downloadDocument(blobUrl: string):void{
+ const anchorElement = document.createElement('a');
+ anchorElement.href = blobUrl;
+ anchorElement.target = '_parent';
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.fileName = this.fileName;
+ const downloadFileName = this.pdfviewerControl.fileName || 'downloadedFile.pdf';
+ anchorElement.download = downloadFileName.endsWith('.pdf') ? downloadFileName : `${downloadFileName}.pdf`;
+ document.body.appendChild(anchorElement);
+ anchorElement.click();
+ document.body.removeChild(anchorElement);
+ URL.revokeObjectURL(blobUrl);
+ }
+ }
+
+ //Method for moving to previous page
+ public previousClicked(e: ClickEventArgs): void {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.navigation.goToPreviousPage();
+ }
+ }
+
+ //Method for moving to next page
+ public nextClicked(e: ClickEventArgs): void {
+ if(this.pdfviewerControl)
+ this.pdfviewerControl.navigation.goToNextPage();
+ }
+
+ //to get current page
+ public pageChanged(e: PageChangeEventArgs): void {
+ if(this.pdfviewerControl)
+ (document.getElementById('e-pv-redact-sb-currentPage') as HTMLSpanElement).textContent = this.pdfviewerControl.currentPageNumber.toString() + ' ';
+ this.updatePageNavigation();
+ }
+
+ public onLoad(e: CreateArgs)
+ {
+ (document.getElementById('e-pv-redact-sb-drop-area-wrap') as HTMLDivElement).style.display="flex";
+ (document.getElementById('e-pv-redact-sb-appbar') as HTMLDivElement).style.display="block";
+ }
+
+ //while loading document
+ public documentLoaded(e: LoadEventArgs): void {
+ const fileUploadElement = document.getElementById('e-pv-redact-sb-fileUpload');
+ if(fileUploadElement && this.pdfviewerControl){
+ fileUploadElement.textContent = '/ ' + this.pdfviewerControl.pageCount;
+ (document.getElementById('e-pv-redact-sb-currentPage') as HTMLSpanElement).textContent = this.pdfviewerControl.currentPageNumber.toString() + ' ';
+ this.updatePageNavigation();
+ this.updateRedaction();
+ }
+ }
+
+
+ //Zoom values changes when the percentage is selected from the dropdown
+ public zoom: any;
+ public previousZoom: any;
+ public zoomValueChange(e: ChangeEventArgs) {
+ this.zoom = (e as any).value;
+ this.previousZoom = (e as any).previousItemData.value;
+ if (this.zoom !== null || this.previousZoom !== null) {
+ var zoomchange = parseInt(this.zoom.replace("%", ""), 10);
+ if(this.pdfviewerControl)
+ this.pdfviewerControl.magnificationModule.zoomTo(zoomchange);
+ }
+ }
+
+ //Updating the navigation button based on the page number either "enabled" or "disabled"
+ private updatePageNavigation(): void {
+ if(this.pdfviewerControl&& this.secondaryToolbar){
+ if (this.pdfviewerControl.currentPageNumber === 1) {
+ this.secondaryToolbar.items[0].disabled=true;
+ this.secondaryToolbar.items[2].disabled=false;
+ } else if (this.pdfviewerControl.currentPageNumber === this.pdfviewerControl.pageCount) {
+ this.secondaryToolbar.items[0].disabled=false;
+ this.secondaryToolbar.items[2].disabled=true;
+ } else {
+ this.secondaryToolbar.items[0].disabled=false;
+ this.secondaryToolbar.items[2].disabled=false;
+ }
+ }
+ }
+
+ //To redact the pdf in server side using the button click event
+ public redaction(): void {
+ if (this.redactionCount > 0) {
+ if(this.pdfviewerControl){
+ this.pdfviewerControl.saveAsBlob().then((blob) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onload = (e: ProgressEvent) => {
+ const base64String = e.target?.result;
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', this.url, true);
+ xhr.setRequestHeader('Content-type', 'application/json; charset=UTF-8');
+ const requestData = JSON.stringify({ base64String });
+ xhr.onload = () => {
+ if (xhr.status === 200) {
+ if(this.pdfviewerControl)
+ this.pdfviewerControl.load(xhr.responseText, "");
+ }
+ else {
+ console.error('Redaction failed:', xhr.statusText);
+ }
+ };
+ xhr.onerror = function () {
+ console.error('An error occurred during the redaction:', xhr.statusText);
+ };
+ xhr.send(requestData);
+ }
+ });
+ this.redactionCount = 0;
+ this.updateRedaction();
+ }
+ }
+
+}
+}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Angular Client/src/app/app.module.ts b/How to/Redact a PDF Document/Angular Client/src/app/app.module.ts
new file mode 100644
index 0000000..5cccb32
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/app/app.module.ts
@@ -0,0 +1,36 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import {
+ PdfViewerModule,
+ LinkAnnotationService,
+ BookmarkViewService,
+ MagnificationService,
+ ThumbnailViewService,
+ ToolbarService,
+ NavigationService,
+ TextSearchService,
+ TextSelectionService,
+ PrintService
+} from '@syncfusion/ej2-angular-pdfviewer';
+import { AppComponent } from './app.component'; // Ensure AppComponent is a standalone component
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ PdfViewerModule,
+ AppComponent, // Add AppComponent to the imports array
+ ],
+ providers: [
+ LinkAnnotationService,
+ BookmarkViewService,
+ MagnificationService,
+ ThumbnailViewService,
+ ToolbarService,
+ NavigationService,
+ TextSearchService,
+ TextSelectionService,
+ PrintService
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule {}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Angular Client/src/assets/.gitkeep b/How to/Redact a PDF Document/Angular Client/src/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/How to/Redact a PDF Document/Angular Client/src/favicon.ico b/How to/Redact a PDF Document/Angular Client/src/favicon.ico
new file mode 100644
index 0000000..997406a
Binary files /dev/null and b/How to/Redact a PDF Document/Angular Client/src/favicon.ico differ
diff --git a/How to/Redact a PDF Document/Angular Client/src/index.html b/How to/Redact a PDF Document/Angular Client/src/index.html
new file mode 100644
index 0000000..7fa8cd9
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ MyApp
+
+
+
+
+
+
+
+
diff --git a/How to/Redact a PDF Document/Angular Client/src/main.ts b/How to/Redact a PDF Document/Angular Client/src/main.ts
new file mode 100644
index 0000000..c58dc05
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/main.ts
@@ -0,0 +1,7 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/How to/Redact a PDF Document/Angular Client/src/styles.css b/How to/Redact a PDF Document/Angular Client/src/styles.css
new file mode 100644
index 0000000..fc9d8ef
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/src/styles.css
@@ -0,0 +1,10 @@
+/* You can add global styles to this file, and also import other style files */
+@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-pdfviewer/styles/material.css';
+@import '../node_modules/@syncfusion/ej2-notifications/styles/material.css';
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Angular Client/tsconfig.app.json b/How to/Redact a PDF Document/Angular Client/tsconfig.app.json
new file mode 100644
index 0000000..374cc9d
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/tsconfig.app.json
@@ -0,0 +1,14 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/How to/Redact a PDF Document/Angular Client/tsconfig.json b/How to/Redact a PDF Document/Angular Client/tsconfig.json
new file mode 100644
index 0000000..ed966d4
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/tsconfig.json
@@ -0,0 +1,33 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "baseUrl": "./",
+ "outDir": "./dist/out-tsc",
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "sourceMap": true,
+ "declaration": false,
+ "downlevelIteration": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "target": "ES2022",
+ "module": "ES2022",
+ "useDefineForClassFields": false,
+ "lib": [
+ "ES2022",
+ "dom"
+ ]
+ },
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false,
+ "strictInjectionParameters": true,
+ "strictInputAccessModifiers": true,
+ "strictTemplates": true
+ }
+}
diff --git a/How to/Redact a PDF Document/Angular Client/tsconfig.spec.json b/How to/Redact a PDF Document/Angular Client/tsconfig.spec.json
new file mode 100644
index 0000000..be7e9da
--- /dev/null
+++ b/How to/Redact a PDF Document/Angular Client/tsconfig.spec.json
@@ -0,0 +1,14 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "include": [
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice.sln b/How to/Redact a PDF Document/Web Service/ControllerPractice.sln
new file mode 100644
index 0000000..45fbb25
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32901.215
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControllerPractice", "ControllerPractice\ControllerPractice.csproj", "{14100176-83DC-4CB1-87D4-4D71C9E8F177}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ Release-Xml|Any CPU = Release-Xml|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {14100176-83DC-4CB1-87D4-4D71C9E8F177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {14100176-83DC-4CB1-87D4-4D71C9E8F177}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {14100176-83DC-4CB1-87D4-4D71C9E8F177}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {14100176-83DC-4CB1-87D4-4D71C9E8F177}.Release|Any CPU.Build.0 = Release|Any CPU
+ {14100176-83DC-4CB1-87D4-4D71C9E8F177}.Release-Xml|Any CPU.ActiveCfg = Release|Any CPU
+ {14100176-83DC-4CB1-87D4-4D71C9E8F177}.Release-Xml|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {676A0D7F-7DC9-4AC1-B66A-956820E0B388}
+ EndGlobalSection
+EndGlobal
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/.config/dotnet-tools.json b/How to/Redact a PDF Document/Web Service/ControllerPractice/.config/dotnet-tools.json
new file mode 100644
index 0000000..071b4ee
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-ef": {
+ "version": "8.0.8",
+ "commands": [
+ "dotnet-ef"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/ControllerPractice.csproj b/How to/Redact a PDF Document/Web Service/ControllerPractice/ControllerPractice.csproj
new file mode 100644
index 0000000..b8b8b60
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/ControllerPractice.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net7.0
+ 0362a66d-0a9d-4b52-8e0a-6e09469f5c00
+
+
+
+
+
+
+
+
+
+
+
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Controllers/HomeController.cs b/How to/Redact a PDF Document/Web Service/ControllerPractice/Controllers/HomeController.cs
new file mode 100644
index 0000000..6ca69f4
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Controllers/HomeController.cs
@@ -0,0 +1,37 @@
+using ControllerPractice.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace ControllerPractice.Controllers
+{
+ public class HomeController : Controller
+ {
+ private readonly ILogger _logger;
+
+ public HomeController(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public IActionResult Index()
+ {
+ return View();
+ }
+
+ public IActionResult Privacy()
+ {
+ return View();
+ }
+
+ [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
+ public IActionResult Error()
+ {
+ return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
+ }
+ }
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Controllers/PdfViewerController.cs b/How to/Redact a PDF Document/Web Service/ControllerPractice/Controllers/PdfViewerController.cs
new file mode 100644
index 0000000..097fbd2
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Controllers/PdfViewerController.cs
@@ -0,0 +1,221 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Memory;
+using Newtonsoft.Json;
+using Syncfusion.EJ2.PdfViewer;
+using Syncfusion.Pdf.Parsing;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Xml.Linq;
+using Azure.Storage.Blobs;
+using Azure.Storage.Blobs.Specialized;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Configuration;
+using Google.Cloud.Storage.V1;
+using Google.Apis.Auth.OAuth2;
+using Syncfusion.Pdf.Graphics;
+using Syncfusion.Pdf.Security;
+using Syncfusion.Pdf;
+using Syncfusion.Pdf.Interactive;
+using Syncfusion.Pdf.Redaction;
+using Syncfusion.Drawing;
+
+namespace PdfViewerService2.Controllers
+{
+ [Route("[controller]")]
+ [ApiController]
+ public class PdfViewerController : ControllerBase
+ {
+ private IHostingEnvironment _hostingEnvironment;
+ //Initialize the memory cache object
+ public IMemoryCache _cache;
+ private IConfiguration _configuration;
+ private readonly ILogger _logger;
+
+ public PdfViewerController(IHostingEnvironment hostingEnvironment, IMemoryCache cache, IConfiguration configuration, ILogger logger)
+ {
+ _hostingEnvironment = hostingEnvironment;
+ _cache = cache;
+ _logger = logger;
+ }
+
+ [HttpPost("Redaction")]
+ /* [Microsoft.AspNetCore.Cors.EnableCors("MyPolicy")]*/
+ [Route("[controller]/Redaction")]
+
+ public IActionResult Redaction([FromBody] Dictionary jsonObject)
+ {
+ string RedactionText = "Redacted";
+ var finalbase64 = string.Empty;
+ PdfRenderer pdfviewer = new PdfRenderer(_cache);
+ string documentBase = pdfviewer.GetDocumentAsBase64(jsonObject);
+ string base64String = documentBase.Split(new string[] { "data:application/pdf;base64," }, StringSplitOptions.None)[1];
+ if (base64String != null || base64String != string.Empty)
+ {
+ byte[] byteArray = Convert.FromBase64String(base64String);
+ Console.WriteLine("redaction");
+ PdfLoadedDocument loadedDocument = new PdfLoadedDocument(byteArray);
+ foreach (PdfLoadedPage loadedPage in loadedDocument.Pages)
+ {
+ List removeItems = new List();
+ foreach (PdfLoadedAnnotation annotation in loadedPage.Annotations)
+ {
+ if (annotation is PdfLoadedRectangleAnnotation)
+ {
+ if (annotation.Author == "Redaction")
+ {
+ // Add the annotation to the removeItems list
+ removeItems.Add(annotation);
+ // Create a new redaction with the annotation bounds and color
+ PdfRedaction redaction = new PdfRedaction(annotation.Bounds, annotation.Color);
+ // Add the redaction to the page
+ loadedPage.AddRedaction(redaction);
+ annotation.Flatten = true;
+ }
+ if (annotation.Author == "Text")
+ {
+ // Add the annotation to the removeItems list
+ removeItems.Add(annotation);
+ // Create a new redaction with the annotation bounds and color
+ PdfRedaction redaction = new PdfRedaction(annotation.Bounds);
+ //Set the font family and font size
+ PdfStandardFont font = new PdfStandardFont(PdfFontFamily.Courier, 8);
+ //Create the appearance like repeated text in the redaction area
+ CreateRedactionAppearance(redaction.Appearance.Graphics, PdfTextAlignment.Left, true, new SizeF(annotation.Bounds.Width, annotation.Bounds.Height), RedactionText, font, PdfBrushes.Red);
+ // Add the redaction to the page
+ loadedPage.AddRedaction(redaction);
+ annotation.Flatten = true;
+ }
+ //Apply the pattern for the Redaction
+ if (annotation.Author == "Pattern")
+ {
+ // Add the annotation to the removeItems list
+ removeItems.Add(annotation);
+ // Create a new redaction with the annotation bounds and color
+ PdfRedaction redaction = new PdfRedaction(annotation.Bounds);
+ Syncfusion.Drawing.RectangleF rect = new Syncfusion.Drawing.RectangleF(0, 0, 8, 8);
+ PdfTilingBrush tillingBrush = new PdfTilingBrush(rect);
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.Gray, new Syncfusion.Drawing.RectangleF(0, 0, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.White, new Syncfusion.Drawing.RectangleF(2, 0, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.LightGray, new Syncfusion.Drawing.RectangleF(4, 0, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.DarkGray, new Syncfusion.Drawing.RectangleF(6, 0, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.White, new Syncfusion.Drawing.RectangleF(0, 2, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.LightGray, new Syncfusion.Drawing.RectangleF(2, 2, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.Black, new Syncfusion.Drawing.RectangleF(4, 2, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.LightGray, new Syncfusion.Drawing.RectangleF(6, 2, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.LightGray, new Syncfusion.Drawing.RectangleF(0, 4, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.DarkGray, new Syncfusion.Drawing.RectangleF(2, 4, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.LightGray, new Syncfusion.Drawing.RectangleF(4, 4, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.White, new Syncfusion.Drawing.RectangleF(6, 4, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.Black, new Syncfusion.Drawing.RectangleF(0, 6, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.LightGray, new Syncfusion.Drawing.RectangleF(2, 6, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.Black, new Syncfusion.Drawing.RectangleF(4, 6, 2, 2));
+ tillingBrush.Graphics.DrawRectangle(PdfBrushes.DarkGray, new Syncfusion.Drawing.RectangleF(6, 6, 2, 2));
+ rect = new Syncfusion.Drawing.RectangleF(0, 0, 16, 14);
+ PdfTilingBrush tillingBrushNew = new PdfTilingBrush(rect);
+ tillingBrushNew.Graphics.DrawRectangle(tillingBrush, rect);
+ //Set the pattern for the redaction area
+ redaction.Appearance.Graphics.DrawRectangle(tillingBrushNew, new Syncfusion.Drawing.RectangleF(0, 0, annotation.Bounds.Width, annotation.Bounds.Height));
+ // Add the redaction to the page
+ loadedPage.AddRedaction(redaction);
+ annotation.Flatten = true;
+ }
+ }
+ else if (annotation is PdfLoadedRubberStampAnnotation)
+ {
+ if (annotation.Author == "Image")
+ {
+ Stream[] images = PdfLoadedRubberStampAnnotationExtension.GetImages(annotation as PdfLoadedRubberStampAnnotation);
+ // Create a new redaction with the annotation bounds and color
+ PdfRedaction redaction = new PdfRedaction(annotation.Bounds);
+ images[0].Position = 0;
+ PdfImage image = new PdfBitmap(images[0]);
+ //Apply the image to redaction area
+ redaction.Appearance.Graphics.DrawImage(image, new Syncfusion.Drawing.RectangleF(0, 0, annotation.Bounds.Width, annotation.Bounds.Height));
+ // Add the redaction to the page
+ loadedPage.AddRedaction(redaction);
+ annotation.Flatten = true;
+ }
+ }
+ }
+ foreach (PdfLoadedAnnotation annotation1 in removeItems)
+ {
+ loadedPage.Annotations.Remove(annotation1);
+ }
+ }
+ loadedDocument.Redact();
+ MemoryStream stream = new MemoryStream();
+ loadedDocument.Save(stream);
+ stream.Position = 0;
+ loadedDocument.Close(true);
+ byteArray = stream.ToArray();
+ finalbase64 = "data:application/pdf;base64," + Convert.ToBase64String(byteArray);
+ stream.Dispose();
+ return Content(finalbase64);
+ //}
+ }
+ return Content("data:application/pdf;base64," + "");
+ }
+
+ private static void CreateRedactionAppearance(PdfGraphics graphics, PdfTextAlignment alignment, bool repeat, SizeF size, string overlayText, PdfFont font, PdfBrush textcolor)
+ {
+ float col = 0, row;
+ if (font == null) font = new PdfStandardFont(PdfFontFamily.Helvetica, 10);
+ int textAlignment = Convert.ToInt32(alignment);
+ float y = 0, x = 0, diff = 0;
+ Syncfusion.Drawing.RectangleF rect;
+ Syncfusion.Drawing.SizeF textsize = font.MeasureString(overlayText);
+
+ if (repeat)
+ {
+ col = size.Width / textsize.Width;
+ row = (float)Math.Floor(size.Height / font.Size);
+ diff = Math.Abs(size.Width - (float)(Math.Floor(col) * textsize.Width));
+ if (textAlignment == 1)
+ x = diff / 2;
+ if (textAlignment == 2)
+ x = diff;
+ for (int i = 1; i < col; i++)
+ {
+ for (int j = 0; j < row; j++)
+ {
+ rect = new Syncfusion.Drawing.RectangleF(x, y, 0, 0);
+ graphics.DrawString(overlayText, font, textcolor, rect);
+ y = y + font.Size;
+ }
+ x = x + textsize.Width;
+ y = 0;
+ }
+ }
+ else
+ {
+ diff = Math.Abs(size.Width - textsize.Width);
+ if (textAlignment == 1)
+ {
+ x = diff / 2;
+ }
+ if (textAlignment == 2)
+ {
+ x = diff;
+ }
+ rect = new Syncfusion.Drawing.RectangleF(x, 0, 0, 0);
+ graphics.DrawString(overlayText, font, textcolor, rect);
+ }
+ }
+ //GET api/values
+ [HttpGet]
+ public IEnumerable Get()
+ {
+ return new string[] { "value1", "value2" };
+ }
+
+ // GET api/values/5
+ [HttpGet("{id}")]
+ public string Get(int id)
+ {
+ return "value";
+ }
+ }
+}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Data/Form2.pdf b/How to/Redact a PDF Document/Web Service/ControllerPractice/Data/Form2.pdf
new file mode 100644
index 0000000..056da51
Binary files /dev/null and b/How to/Redact a PDF Document/Web Service/ControllerPractice/Data/Form2.pdf differ
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Models/ErrorViewModel.cs b/How to/Redact a PDF Document/Web Service/ControllerPractice/Models/ErrorViewModel.cs
new file mode 100644
index 0000000..2cb694c
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Models/ErrorViewModel.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace ControllerPractice.Models
+{
+ public class ErrorViewModel
+ {
+ public string RequestId { get; set; }
+
+ public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
+ }
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Program.cs b/How to/Redact a PDF Document/Web Service/ControllerPractice/Program.cs
new file mode 100644
index 0000000..549d8e6
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Program.cs
@@ -0,0 +1,26 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace ControllerPractice
+{
+ 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();
+ });
+ }
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/PublishProfiles/ControllerPractice20240904205 - Web Deploy.pubxml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/PublishProfiles/ControllerPractice20240904205 - Web Deploy.pubxml
new file mode 100644
index 0000000..dd924af
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/PublishProfiles/ControllerPractice20240904205 - Web Deploy.pubxml
@@ -0,0 +1,30 @@
+
+
+
+
+ MSDeploy
+ /subscriptions/2478e6de-4596-42f5-957e-f700933a09f0/resourcegroups/client_group/providers/Microsoft.Web/sites/ControllerPractice20240904205
+ client_group
+ AzureWebSite
+ Release
+ Any CPU
+ https://controllerpractice20240904205.azurewebsites.net
+ true
+ false
+ 14100176-83dc-4cb1-87d4-4d71c9e8f177
+ controllerpractice20240904205.scm.azurewebsites.net:443
+ ControllerPractice20240904205
+
+ true
+ WMSVC
+ true
+ true
+ $ControllerPractice20240904205
+ <_SavePWD>true
+ <_DestinationType>AzureWebSite
+ false
+
+
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/PublishProfiles/FolderProfile.pubxml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 0000000..9e16d5e
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,21 @@
+
+
+
+
+ true
+ false
+ true
+ Release
+ Any CPU
+ FileSystem
+ D:\Application IIS Express published
+ FileSystem
+ <_TargetId>Folder
+
+ net7.0
+ 14100176-83dc-4cb1-87d4-4d71c9e8f177
+ false
+
+
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/ServiceDependencies/ControllerPractice20240904205 - Web Deploy/profile.arm.json b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/ServiceDependencies/ControllerPractice20240904205 - Web Deploy/profile.arm.json
new file mode 100644
index 0000000..acec296
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/ServiceDependencies/ControllerPractice20240904205 - Web Deploy/profile.arm.json
@@ -0,0 +1,113 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_dependencyType": "compute.appService.windows"
+ },
+ "parameters": {
+ "resourceGroupName": {
+ "type": "string",
+ "defaultValue": "client_group",
+ "metadata": {
+ "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking."
+ }
+ },
+ "resourceGroupLocation": {
+ "type": "string",
+ "defaultValue": "eastus",
+ "metadata": {
+ "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support."
+ }
+ },
+ "resourceName": {
+ "type": "string",
+ "defaultValue": "ControllerPractice20240904205",
+ "metadata": {
+ "description": "Name of the main resource to be created by this template."
+ }
+ },
+ "resourceLocation": {
+ "type": "string",
+ "defaultValue": "[parameters('resourceGroupLocation')]",
+ "metadata": {
+ "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there."
+ }
+ }
+ },
+ "variables": {
+ "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+ "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]"
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Resources/resourceGroups",
+ "name": "[parameters('resourceGroupName')]",
+ "location": "[parameters('resourceGroupLocation')]",
+ "apiVersion": "2019-10-01"
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+ "resourceGroup": "[parameters('resourceGroupName')]",
+ "apiVersion": "2019-10-01",
+ "dependsOn": [
+ "[parameters('resourceGroupName')]"
+ ],
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [
+ {
+ "location": "[parameters('resourceLocation')]",
+ "name": "[parameters('resourceName')]",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2015-08-01",
+ "tags": {
+ "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty"
+ },
+ "dependsOn": [
+ "[variables('appServicePlan_ResourceId')]"
+ ],
+ "kind": "app",
+ "properties": {
+ "name": "[parameters('resourceName')]",
+ "kind": "app",
+ "httpsOnly": true,
+ "reserved": false,
+ "serverFarmId": "[variables('appServicePlan_ResourceId')]",
+ "siteConfig": {
+ "metadata": [
+ {
+ "name": "CURRENT_STACK",
+ "value": "dotnetcore"
+ }
+ ]
+ }
+ },
+ "identity": {
+ "type": "SystemAssigned"
+ }
+ },
+ {
+ "location": "[parameters('resourceLocation')]",
+ "name": "[variables('appServicePlan_name')]",
+ "type": "Microsoft.Web/serverFarms",
+ "apiVersion": "2015-08-01",
+ "sku": {
+ "name": "S1",
+ "tier": "Standard",
+ "family": "S",
+ "size": "S1"
+ },
+ "properties": {
+ "name": "[variables('appServicePlan_name')]"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/launchSettings.json b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/launchSettings.json
new file mode 100644
index 0000000..01074e4
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Properties/launchSettings.json
@@ -0,0 +1,39 @@
+{
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "pdfviewer",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "ControllerPractice": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "dotnetRunMessages": "true",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ },
+ "WSL": {
+ "commandName": "WSL2",
+ "launchBrowser": true,
+ "launchUrl": "https://localhost:5001",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "https://localhost:5001;http://localhost:5000"
+ },
+ "distributionName": ""
+ }
+ },
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:7001",
+ "sslPort": 44309
+ }
+ }
+}
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Startup.cs b/How to/Redact a PDF Document/Web Service/ControllerPractice/Startup.cs
new file mode 100644
index 0000000..3a6a57d
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Startup.cs
@@ -0,0 +1,85 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.ResponseCompression;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Serialization;
+
+namespace ControllerPractice
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ readonly string MyAllowSpecificOrigins = "MyPolicy";
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllersWithViews();
+ services.AddMemoryCache();
+ services.AddControllers().AddNewtonsoftJson(options =>
+ {
+ // Use the default property (Pascal) casing
+ options.SerializerSettings.ContractResolver = new DefaultContractResolver();
+ });
+
+ services.AddCors(options =>
+ {
+ options.AddPolicy(MyAllowSpecificOrigins,
+ builder =>
+ {
+ builder.AllowAnyOrigin()
+ .AllowAnyMethod()
+ .AllowAnyHeader();
+ });
+ });
+ services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
+ services.Configure(options => options.Level = System.IO.Compression.CompressionLevel.Optimal);
+ services.AddResponseCompression();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("ORg4AjUWIQA/Gnt2UlhhQlVMfV5AQmBIYVp/TGpJfl96cVxMZVVBJAtUQF1hTX9TdkJjXn5fc3FQR2dZ");
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Home/Error");
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+ app.UseCors(MyAllowSpecificOrigins);
+ app.UseHttpsRedirection();
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllerRoute(
+ name: "default",
+ pattern: "{controller=Home}/{action=Index}/{id?}");
+ });
+ }
+ }
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Home/Index.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Home/Index.cshtml
new file mode 100644
index 0000000..947d01c
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Home/Index.cshtml
@@ -0,0 +1,14 @@
+@{
+ ViewData["Title"] = "Home Page";
+}
+
+
+
+@* *@
+
+@* *@
+
+
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Home/Privacy.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Home/Privacy.cshtml
new file mode 100644
index 0000000..af4fb19
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Home/Privacy.cshtml
@@ -0,0 +1,6 @@
+@{
+ ViewData["Title"] = "Privacy Policy";
+}
+@ViewData["Title"]
+
+Use this page to detail your site's privacy policy.
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/Error.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/Error.cshtml
new file mode 100644
index 0000000..a1e0478
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/Error.cshtml
@@ -0,0 +1,25 @@
+@model ErrorViewModel
+@{
+ ViewData["Title"] = "Error";
+}
+
+Error.
+An error occurred while processing your request.
+
+@if (Model.ShowRequestId)
+{
+
+ Request ID: @Model.RequestId
+
+}
+
+Development Mode
+
+ Swapping to Development environment will display more detailed information about the error that occurred.
+
+
+ The Development environment shouldn't be enabled for deployed applications.
+ It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
+ and restarting the app.
+
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/_Layout.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/_Layout.cshtml
new file mode 100644
index 0000000..333e08d
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/_Layout.cshtml
@@ -0,0 +1,54 @@
+
+
+
+
+
+ @ViewData["Title"] - ControllerPractice
+
+ @* Syncfusion Essential JS 2 Styles *@
+
+
+ @* Syncfusion Essential JS 2 Scripts *@
+
+
+
+
+
+
+
+ @RenderBody()
+
+
+
+
+
+
+
+ @await RenderSectionAsync("Scripts", required: false)
+
+
+
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/_ValidationScriptsPartial.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/_ValidationScriptsPartial.cshtml
new file mode 100644
index 0000000..5a16d80
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/Shared/_ValidationScriptsPartial.cshtml
@@ -0,0 +1,2 @@
+
+
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/_ViewImports.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/_ViewImports.cshtml
new file mode 100644
index 0000000..be46e80
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/_ViewImports.cshtml
@@ -0,0 +1,4 @@
+@using ControllerPractice
+@using ControllerPractice.Models
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@addTagHelper *, Syncfusion.EJ2
\ No newline at end of file
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/_ViewStart.cshtml b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/_ViewStart.cshtml
new file mode 100644
index 0000000..a5f1004
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/Views/_ViewStart.cshtml
@@ -0,0 +1,3 @@
+@{
+ Layout = "_Layout";
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/appsettings.Development.json b/How to/Redact a PDF Document/Web Service/ControllerPractice/appsettings.Development.json
new file mode 100644
index 0000000..8983e0f
--- /dev/null
+++ b/How to/Redact a PDF Document/Web Service/ControllerPractice/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/How to/Redact a PDF Document/Web Service/ControllerPractice/wwwroot/favicon.ico b/How to/Redact a PDF Document/Web Service/ControllerPractice/wwwroot/favicon.ico
new file mode 100644
index 0000000..63e859b
Binary files /dev/null and b/How to/Redact a PDF Document/Web Service/ControllerPractice/wwwroot/favicon.ico differ