1. Installing Node.js and npm:
Before you can install Angular and its CLI, you need to have Node.js and npm (Node package manager) on your system.
Download and install Node.js from Node.js official website. The npm is included in the installation.
To check if you have Node.js and npm correctly installed, run the following commands:
node -v npm -v
2. Installing the Angular CLI:
Once you have Node.js and npm installed, you can install the Angular CLI globally on your machine:
npm install -g @angular/cli
To verify that the Angular CLI was installed correctly:
ng --version
3. Creating a New Angular Project:
After installing the Angular CLI, you can create a new Angular project using:
ng new your-project-name
Replace your-project-name
with your desired project name. This command will prompt you for some configuration choices. For most beginners, the default options will suffice.
4. Navigating into Your Project:
cd your-project-name
5. Serving Your Application:
To see your new Angular app in action:
ng serve
By default, this will start a development server, and you can view your app by navigating to http://localhost:4200/
in your browser. The app will automatically reload if you make any changes to the source code.
6. Generating Component & Services
ng generate component image-upload
ng generate component result-display
ng generate service Image
ng generate service Ocr
Create new file pdfjs.d.ts under app folder and write this code in the file
declare module 'pdfjs-dist/build/pdf';
7. Required Libraries
Tesseract.js:
Tesseract.js is a pure JavaScript port of the popular Tesseract OCR engine. To install it, run:
npm install tesseract.js
pdfjs-dist:
PDF.js is a Portable Document Format (PDF) library that is built with HTML5. To install it, run:
npm install pdfjs-dist
ImageUpload
Component
Image-Upload.Component.html
<div class="image-upload-area" (click)="triggerInputClick()">
<input #fileInput type="file" (change)="onFileChange($event)" />
<div class="upload-button">Upload | Drag & Drop | Screenshot & Paste Image | Then Hit The Center Button</div>
</div>
<img *ngIf="imageSrc" [src]="imageSrc" alt="Preview" />
<div class="screenshot-instructions" *ngIf="showScreenshotInstructions">
<p>Press Ctrl + PrtSc to capture a screenshot, then paste it here.</p>
</div>
Image-Upload.Component.ts
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, HostListener } from '@angular/core';
import { ImageService } from '../image.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-image-upload',
templateUrl: './image-upload.component.html',
styleUrls: ['./image-upload.component.css']
})
export class ImageUploadComponent implements OnInit, OnDestroy {
imageSrc: string | ArrayBuffer = '';
private imageSubscription!: Subscription;
@ViewChild('fileInput') fileInput!: ElementRef;
showScreenshotInstructions: boolean = true;
constructor(private imageService: ImageService) { }
ngOnInit(): void {
this.imageSubscription = this.imageService.imageSource$.subscribe(imageSrc => {
this.imageSrc = imageSrc;
});
}
triggerInputClick(): void {
this.fileInput.nativeElement.click();
}
onFileChange(event: Event) {
const file = (event.target as HTMLInputElement).files?.[0];
console.log('Selected file:', file);
const reader = new FileReader();
reader.onload = e => {
if (reader.result) {
this.imageService.setImageSource(reader.result);
}
};
if (file) {
reader.readAsDataURL(file);
}
}
@HostListener('document:paste', ['$event'])
onPaste(event: ClipboardEvent) {
if (event.clipboardData) {
const item = event.clipboardData.items[0]; // Assuming the first item is an image
if (item && item.type.indexOf('image') !== -1) {
const blob = item.getAsFile();
if (blob) {
const reader = new FileReader();
reader.onload = e => {
if (reader.result) {
this.imageService.setImageSource(reader.result);
}
};
reader.readAsDataURL(blob);
}
}
}
this.showScreenshotInstructions = false;
}
ngOnDestroy(): void {
// Unsubscribe to prevent memory leaks
this.imageSubscription.unsubscribe();
}
}
Result-DiplayComponent
Result-display.Component.html
<div>
<textarea readonly [value]="resultText"></textarea>
</div>
Result-display.Component.ts
import { Component, Input } from '@angular/core'; // Importing Input here
@Component({
selector: 'app-result-display',
templateUrl: './result-display.component.html',
styleUrls: ['./result-display.component.css']
})
export class ResultDisplayComponent {
@Input() resultText: string = '';
}
App.component.html
<div class="bg_set">
<div class="main-container cont">
<div class="logo"><img src="../assets/logo.png"></div>
</div></div>
<div class="main-container">
<div class="overall_ocr">
<div class="left_data">
<div class="uploader">
<app-image-upload></app-image-upload>
</div>
<!-- Content for the left_data section goes here -->
</div>
<div class="center_btn">
<img src="../assets/convert.svg" (click)="onConvertClick()">
<div *ngIf="isProcessing" class="processing">
Processing...
</div>
</div>
<div class="right_data">
<app-result-display [resultText]="resultText"></app-result-display>
</div>
</div>
<div class="warning">
*For Better And Accurate Result Upload The High Resolution Image
</div>
</div>
<div class="footer">
© 2023 Your imagetotextfree. All rights reserved.
</div>
App.component.ts
import { Component } from '@angular/core';
import { ImageService } from './image.service';
import { OcrService } from './ocr.service';
import { HostListener } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
imageSrc: string | ArrayBuffer = '';
resultText: string = '';
isProcessing: boolean = false; // Corrected property name
constructor(private ocrService: OcrService, private imageService: ImageService) {
this.imageService.imageSource$.subscribe(imageSrc => {
this.imageSrc = imageSrc;
});
}
onConvertClick() {
if (typeof this.imageSrc === 'string') {
this.isProcessing = true; // Corrected property name
this.ocrService.recognizeImage(this.imageSrc).then((result: string) => {
this.resultText = result;
this.isProcessing = false; // Corrected property name
});
}
}
@HostListener('window:keydown', ['$event'])
handleKeyboardEvent(event: KeyboardEvent) {
const key = event.key || String.fromCharCode(event.keyCode);
if ((event.ctrlKey || event.metaKey) && (key === 'u' || key === 'U')) {
event.preventDefault();
console.log('CTRL+U is disabled on this site.');
}
}
}
App.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ImageUploadComponent } from './image-upload/image-upload.component';
import { ResultDisplayComponent } from './result-display/result-display.component';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent,
ImageUploadComponent,
ResultDisplayComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
image.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class ImageService {
private imageSourceSubject = new BehaviorSubject<string | ArrayBuffer>('');
imageSource$ = this.imageSourceSubject.asObservable();
setImageSource(imageSource: string | ArrayBuffer) {
console.log('Setting image source:', imageSource); // Log the new value of imageSource
this.imageSourceSubject.next(imageSource);
}
}
ocr.service.ts
import * as Tesseract from 'tesseract.js';
import * as pdfjsLib from 'pdfjs-dist/build/pdf';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class OcrService {
constructor() {}
private removeSpecialCharacters(text: string): string {
return text.replace(/[^\w\s]/gi, ''); // Replace special characters with empty strings
}
async recognizeImage(imageSrc: string | ArrayBuffer): Promise<string> {
console.log('Starting OCR recognition for:', imageSrc); // Log the starting of recognition
if (typeof imageSrc !== 'string') {
throw new Error('Invalid image source type');
}
// Load the image as an Image object
const image = new Image();
image.src = imageSrc;
console.log('Image object created:', image); // Log the image object
// Ensure the image is loaded before proceeding
await new Promise<HTMLImageElement>((resolve) => {
image.onload = () => resolve(image);
});
// Use Tesseract to recognize the text in the image
const result = await Tesseract.recognize(image, 'eng', {
logger: (progress) => console.log(progress),
});
return result.data.text;
}
async recognizePdf(pdfSrc: string | ArrayBuffer): Promise<string[]> {
console.log('Recognizing PDF with source:', pdfSrc); // Log the starting of PDF recognition
const loadingTask = pdfjsLib.getDocument(pdfSrc);
const pdf = await loadingTask.promise;
const numPages = pdf.numPages;
const pagesText = [];
for (let i = 1; i <= numPages; i++) {
console.log('Processing page:', i); // Log the processing of each page
const page = await pdf.getPage(i);
const canvas = document.createElement('canvas');
const viewport = page.getViewport({ scale: 1 });
canvas.width = viewport.width;
canvas.height = viewport.height;
const renderContext = {
canvasContext: canvas.getContext('2d')!,
viewport: viewport,
};
await page.render(renderContext).promise;
const text = await Tesseract.recognize(canvas, 'eng', {
logger: (progress) => console.log(progress),
});
pagesText.push(text.data.text);
}
return pagesText;
}
}
styles.css
/* You can add global styles to this file, and also import other style files */
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;600&display=swap');
*, ::after, ::before {
box-sizing: border-box;
}
:focus-visible {
outline: 0px !important;
}
body{ font-family: 'Jost', sans-serif; margin: 0px; padding: 0px;}
.container-fluid{ width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;}
.overall_ocr {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 40px;
width: 90%;
margin: 0 auto;
border-radius: 10px;
margin-top: -270px;
box-shadow: rgba(35, 55, 80, 0.1) 0px 6px 12px;
position: relative;
}
.left_data {
width: 45%;
border-radius: 8px;
background-color: rgb(255, 255, 255);
box-shadow: rgba(35, 55, 80, 0.3) 0px 6px 12px;
padding: 20px;
min-height: 500px;
}
.right_data{ width: 45%;
border-radius: 8px;
background-color: rgb(255, 255, 255);
box-shadow: rgba(35, 55, 80, 0.3) 0px 6px 12px;
padding: 20px; min-height: 500px;}
.center_btn{ width: 8%;}
.right_data textarea {
display: block;
width: 100%;
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: var(--bs-body-color);
-webkit-appearance: none;
appearance: none;
background-color: var(--bs-body-bg);
background-clip: padding-box;
border: var(--bs-border-width) solid var(--bs-border-color);
border-radius: var(--bs-border-radius);
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
resize: vertical;
height: 500px;
}
.center_btn img{ background: #fff;
padding: 20px;
border-radius: 100px; border: 2px solid #0a146e; cursor: pointer;}
.center_btn {
width: 8%;
text-align: center;
}
.processing {
color: #212121;
}
/* Style for the image upload container */
.image-upload-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh; /* Adjust this to your preference */
}
/* Style for the image upload area */
.image-upload-area {
text-align: center;
border: 2px dashed #f56e23fc;
padding: 20px;
cursor: pointer;
}
/* Style for the custom file upload button */
.upload-button {
background-color: #f56e23fc;
color: white;
padding: 17px 20px;
border-radius: 4px;
font-size: 18px;
}
/* Change background color on hover */
.upload-button:hover {
background-color: #0a146e;
}
/* Hide the input file element */
input[type="file"] {
display: none;
}
/* Style for the uploaded image */
.left_data img {
display: block;
margin: 0 auto;
max-width: 100%;
border: 1px solid #ccc;
border-radius: 4px;
}
.bg_set {
background: #0a146e;
width: 100%;
padding: 50px;
padding-top: 180px;
min-height: 450px;
}
.left_data {
width: 45%;
border-radius: 8px;
background-color: rgb(255, 255, 255);
box-shadow: rgba(35, 55, 80, 0.3) 0px 6px 12px;
padding: 20px;
min-height: 500px;
}
.left_data img{ max-height: 480px;}
.image-upload-area {
position: absolute;
top: -120px;
left: 0;
}
.upload-button:hover {
background-color: #fff;
color: #212121;
}
.screenshot-instructions p {
margin-top: -60px;
text-align: center;
position: absolute;
top: 78px;
width: 41%;
}
.footer {
background: #0a146e;
padding: 20px 0px;
margin-top: 70px; color: #fff; text-align: center;
}
.warning {
text-align: right;
margin-top: 20px;
margin-right: 90px;
}
.bg_set img {
position: absolute;
right: 38px;
top: -141px;
width: 100px;
}
.cont{ position: relative;}
/* responsive */
@media (min-width: 320px) and (max-width: 767px) {
.overall_ocr {
display: block;
}
}