Generate searchable PDFs from scanned documents or images using OCR (Optical Character Recognition). The scanned file that contains characters is converted into machine-readable searchable and selectable text as a PDF document.
This sample code includes Server SDK processing in JavaScript, with UI provided by WebViewer. If a viewer is not needed, or you want to work with a different language or framework for the Server SDK, please check out our Server SDK OCR Sample Code.
This demo allows you to:
To add OCR Module capability with Server SDK, and view with WebViewer:
Step 1: Follow get-started in JavaScript for Server SDK
Step 2: Follow get-started in your preferred web stack for WebViewer
Step 3: Download OCR Module
Step 4: Add the ES6 JavaScript sample code provided in this guide
Note: Only the first page is processed in this demo.
Once you generate your license key, it will automatically be included in your sample code below.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
1// ES6 Compliant Syntax
2// GitHub Copilot v1.0 - GPT-4 Model - September 24, 2024
3// File: ocr-module/client/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
8
9function initializeWebViewer() {
10
11 console.log('Initializing WebViewer...');
12 const viewerElement = document.getElementById('viewer');
13 if (!viewerElement) {
14 console.error('❌ Viewer element not found. Please ensure there is a div with id "viewer" in your HTML.');
15 return;
16 }
17
18 WebViewer({
19 path: '/lib',
20 initialDoc: UIElements.demoFilesPath + UIElements.ocrdemofiles['eng'],
21 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
22 enableMeasurement: false,
23 loadAsPDF: true,
24 licenseKey: licenseKey,
25 }, viewerElement).then(instance => {
26
27 // Once the PDF document is loaded, send it to the server.
28 // The sent PDF document will be processed by the server,
29 // by extracting form fields JSON data when the user clicks the "Detect Form Fields" button.
30 instance.Core.documentViewer.addEventListener('documentLoaded', async () => {
31 if (!UIElements.loadingFromList) {
32 UIElements.loadedFromPicker = true;
33 UIElements.loadLabelText = ' Use file picker to load your own file';
34 }
35
36 // if next time we load from list, this will be set to true again
37 UIElements.loadingFromList = false;
38
39 // Customize the main webviewer left panel after loading completes
40 UIElements.customizeUI(instance, performOCR);
41 });
42 console.log('✅ WebViewer loaded successfully.');
43 }).catch((error) => {
44 console.error('Failed to initialize WebViewer:', error);
45 });
46}
47
48// Perform OCR by sending the current PDF page to the server
49const performOCR = async (instance) => {
50 // Reset JSON data
51 UIElements.jsonData = null;
52 let resultText = '';
53
54 // Preparation of the PDF blob to be sent to the server
55 const doc = instance.Core.documentViewer.getDocument();
56 const currentPage = instance.Core.documentViewer.getCurrentPage();
57 const xfdfString = await instance.Core.annotationManager.exportAnnotations(); // obtaining annotations in the loaded document
58 const data = await doc.getFileData({ xfdfString });
59 const arr = new Uint8Array(data);
60 const blob = new Blob([arr], { type: 'application/pdf' });
61 const formData = new FormData();
62 formData.append(doc.filename, blob, doc.filename);
63 // Send the PDF blob to the server for processing
64 new Promise(function (resolve, reject) {
65 console.log('🚀 Sending PDF to server for processing...');
66 fetch(`http://localhost:5050/server/handler.js?filename=${doc.filename}¤tPage=${currentPage}&lang=${UIElements.selectedLanguage}`, {
67 method: 'POST',
68 body: formData,
69 }).then(function (response) {
70
71 if (response.status === 200) {
72 response.text().then(function (json) {
73 UIElements.jsonData = json;
74 const ocrResult = JSON.parse(UIElements.jsonData);
75
76 for (const page of ocrResult.Page) {
77 for (const para of page.Para) {
78 for (const line of para.Line) {
79 for (const word of line.Word) {
80 resultText += word.text + ' ';
81 }
82 resultText += '\n';
83 }
84 }
85 }
86 resolve();
87 })
88 } else {
89 const errorText = `❌ Server responded with status: ${response.status}`;
90 resultText = errorText + resultText;
91 console.error(resultText);
92 reject(new Error(`Server error: ${response.status}`));
93 }
94 }).catch(function (error) {
95 let errorText = '❌ Failed to connect to server: ' + error;
96 errorText += '\n📍 Attempted URL: http://localhost:5050/server/handler.js';
97 errorText += '\n🔍 This likely means the OCR server is not running on port 5050';
98 console.error(errorText);
99 resultText = errorText + resultText;
100 reject(error);
101 });
102 }).catch(function (error) {
103 const errorText = '❌ Error in PDF upload promise: ' + error;
104 console.error(errorText);
105 resultText = errorText + resultText;
106 }).finally(function () {
107 UIElements.ocrText.textContent = resultText;
108 UIElements.setLoading(instance, false);
109 });
110}
111
112
113function loadUIElementsScript() {
114 return new Promise((resolve, reject) => {
115 if (window.UIElements) {
116 console.log('UIElements already loaded');
117 resolve();
118 return;
119 }
120
121 const script = document.createElement('script');
122 script.src = '/showcase-demos/ocr-module/client/ui-elements.js';
123 script.onload = function () {
124 console.log('✅ UIElements script loaded successfully');
125 resolve();
126 };
127 script.onerror = function () {
128 console.error('Failed to load UIElements script');
129 reject(new Error('Failed to load ui-elements.js'));
130 };
131 document.head.appendChild(script);
132 });
133}
134
135// Load UIElements script first, then initialize WebViewer
136loadUIElementsScript().then(() => {
137 initializeWebViewer();
138}).catch((error) => {
139 console.error('Failed to load UIElements:', error);
140});
1const { PDFNet } = require('@pdftron/pdfnet-node');
2const path = require('path');
3const fs = require('fs');
4
5// **Important**
6// You must get a license key from Apryse for the server to run.
7// A trial key can be obtained from:
8// https://docs.apryse.com/core/guides/get-started/trial-key
9const licenseKey = 'YOUR_SERVER_LICENSE_KEY';
10const multer = require('multer');
11const { response } = require('express');
12const upload = multer();
13const serverFolder = 'server';
14const sentImages = 'sentImages';
15const serverHandler = `/${serverFolder}/handler.js`;
16
17module.exports = async (app) => {
18
19 async function initializeServer() {
20 const ocrOK = await PDFNet.OCRModule.isModuleAvailable();
21 if (!ocrOK) {
22 console.log('\nUnable to run OCR Demo: Apryse SDK OCR module not available.');
23 console.log('---------------------------------------------------------------');
24 console.log('The OCR module is an optional add-on, available for download');
25 console.log('at https://docs.apryse.com/core/guides/info/modules#ocr-module . If you have already downloaded this');
26 console.log('module, ensure that the SDK is able to find the required files');
27 console.log('using the PDFNet.addResourceSearchPath() function.\n');
28 }
29 else {
30 console.log('Apryse SDK OCR module is available.');
31 if (!fs.existsSync(sentImages))
32 fs.mkdirSync(sentImages);
33 }
34 }
35
36 // Handle POST request sent to '/server/handler.js'
37 // This endpoint receives the currently loaded PDF file in the Apryse webviewer, converts the current page to an image,
38 // recognizes text from the image, then sends it back to the client as JSON data
39 app.post(serverHandler, upload.any(), async (request, response) => {
40 const sentPdf = path.resolve(__dirname, `./${sentImages.split('/').pop()}/${request.query.filename}`);
41 const currentPage = request.query.currentPage ? parseInt(request.query.currentPage) : 1;
42 const draw = await PDFNet.PDFDraw.create(); // PDFDraw class is used to rasterize PDF pages.
43 const doc = await PDFNet.PDFDoc.createFromBuffer(request.files[0].buffer);
44 draw.setDPI(300);
45 const ocrPage = await (await doc.getPageIterator(currentPage)).current();
46 const imageName = sentPdf + `_page${currentPage}.png`;
47 await draw.export(ocrPage, imageName);
48
49 const opts = new PDFNet.OCRModule.OCROptions();
50 const useIRIS = await PDFNet.OCRModule.isIRISModuleAvailable();
51 if(useIRIS) opts.setOCREngine('iris');
52 let json = null;
53 response.header('Content-Type', 'application/json');
54 try {
55 const doc = await PDFNet.PDFDoc.create();
56 opts.addLang(request.query.lang || 'eng');
57 json = await PDFNet.OCRModule.getOCRJsonFromImage(doc, imageName, opts);
58 await fs.promises.unlink(imageName); // delete the image after OCR
59 //json = await PDFNet.OCRModule.getOCRJsonFromPDF(doc, opts);
60 response.status(200).send(json);
61 } catch (e) {
62 response.status(500).send(`Error extracting JSON text from PDF file ${request.query.filename}`);
63 }
64 });
65
66 // Initialize PDFNet
67 PDFNet.runWithoutCleanup(initializeServer, licenseKey).then(
68 function onFulfilled() {
69 response.status(200);
70 },
71 function onRejected(error) {
72 // log error and close response
73 console.error('Error initializing PDFNet', error);
74 response.status(503).send();
75 }
76 );
77};
1
2const express = require('express');
3const fs = require('fs');
4const bodyParser = require('body-parser');
5const open = (...args) => import('open').then(({ default: open }) => open(...args));
6const handler = require('./handler.js');
7const port = process.env.PORT || 5050;
8const app = express();
9const sentPdfs = 'sentPdfs';
10
11// CORS middleware to allow cross-origin requests from the playground
12app.use((req, res, next) => {
13 res.header('Access-Control-Allow-Origin', '*');
14 res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
15 res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
16
17 // Handle preflight OPTIONS requests
18 if (req.method === 'OPTIONS') {
19 res.sendStatus(200);
20 } else {
21 next();
22 }
23});
24
25app.use(bodyParser.text());
26app.use('/client', express.static('../client')); // For statically serving 'client' folder at '/'
27
28handler(app);
29
30// Run server
31const server = app.listen(port, 'localhost', (err) => {
32 if (err) {
33 console.error(err);
34 } else {
35 console.info(`Server is listening at http://localhost:${port}`);
36
37 }
38});
39
40// Server shutdown and cleanup
41function shutdown() {
42 console.log('Cleanup started...');
43
44 // Example: Close server
45 server.close(() => {
46 console.log('Server closed.');
47
48 // Removes sent PDFs folder
49 if (fs.existsSync(sentPdfs))
50 fs.rmdirSync(sentPdfs, { recursive: true });
51
52 // If no async cleanup, exit directly
53 process.exit(0);
54 });
55}
56
57// Handle shutdown signals
58process.on('SIGINT', shutdown); // Ctrl+C
59process.on('SIGTERM', shutdown); // kill command or Docker stop
60process.on('uncaughtException', (err) => {
61 console.error('Uncaught Exception:', err);
62 shutdown();
63});
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales