How to Create an Online PDF Reader with Download and Print Options Using HTML, CSS, and JavaScript

Features Implemented
Core Features :

  • PDF Upload and Viewing: Users can upload PDF files which are rendered in the browser using PDF.js
  • Page Rendering: Pages are rendered using canvas elements
  • View Modes: Supports both single-page view and all-pages scrollable view

User Options :

  • Print: Opens the browser's print dialog for printing the PDF
  • Save as Images: Converts all pages to JPG images and packages them in a ZIP file for download

Basic Controls :

  • Navigation: Previous/Next buttons for moving between pages
  • Page Info: Displays current page number and total pages (e.g., "Page 3 of 10")
  • Zoom: Zoom in/out functionality with percentage display
  • Technical Implementation
    PDF.js: Used for PDF rendering and processing
  • JSZip: For creating ZIP archives of the images
  • Canvas-to-Blob: For converting canvas elements to image files
  • FileSaver.js: For triggering the file download

UI Features :

  • Responsive Design: Works on both desktop and mobile devices
  • Modern Styling: Uses Inter font and clean, intuitive interface
  • Font Awesome Icons: For better visual cues
  • Loading States: Visual feedback during processing

The application is completely self-contained - just copy and paste the code into an HTML file and it will work without any additional dependencies.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>PDF Reader</title> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script> </head> <body> <div class="container"> <header> <h1><i class="fas fa-file-pdf"></i> PDF Reader</h1> <p>Upload and view PDF files in your browser</p> </header> <div class="upload-section"> <div class="file-input-wrapper"> <button class="file-input-label"> <i class="fas fa-upload"></i> Choose PDF File </button> <input type="file" id="file-input" class="file-input" accept=".pdf"> </div> <div class="file-name" id="file-name">No file selected</div> </div> <div class="viewer-section" id="viewer-section"> <div class="toolbar"> <div class="toolbar-group"> <button class="btn btn-danger" id="close-btn"> <i class="fas fa-times"></i> Close </button> </div> <div class="toolbar-group"> <button class="btn" id="print-btn"> <i class="fas fa-print"></i> Print </button> <button class="btn" id="save-images-btn"> <i class="fas fa-file-archive"></i> Save as Images (ZIP) </button> </div> </div> <div class="toolbar"> <div class="page-controls"> <button class="btn" id="prev-page"> <i class="fas fa-chevron-left"></i> Prev </button> <span class="page-info" id="page-info">Page 1 of 1</span> <button class="btn" id="next-page"> Next <i class="fas fa-chevron-right"></i> </button> </div> <div class="zoom-controls"> <button class="btn" id="zoom-out"> <i class="fas fa-search-minus"></i> </button> <span class="zoom-level" id="zoom-level">100%</span> <button class="btn" id="zoom-in"> <i class="fas fa-search-plus"></i> </button> </div> </div> <div class="view-mode-selector"> <button class="view-mode-btn active" id="single-page-btn">Single Page</button> <button class="view-mode-btn" id="all-pages-btn">All Pages</button> </div> <div id="single-page-container"> <div class="canvas-container"> <canvas id="pdf-canvas"></canvas> </div> </div> <div id="all-pages-container"></div> </div> <footer> <p>PDF Reader using PDF.js | © <span id="year"></span></p> </footer> </div> </body> </html>
CSS provides style to an HTML page. To make the page attractive create a CSS file with the name style.css and remember that you have to make a file with a .css extension.


:root {
            --primary-color: #4a6fa5;
            --secondary-color: #6c757d;
            --light-color: #f8f9fa;
            --dark-color: #343a40;
            --border-color: #dee2e6;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Inter', sans-serif;
        }

        body {
            background-color: #f5f5f5;
            color: #333;
            line-height: 1.6;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding-bottom: 20px;
            border-bottom: 1px solid var(--border-color);
        }

        h1 {
            font-size: 2rem;
            margin-bottom: 10px;
            color: var(--primary-color);
        }

        .upload-section {
            background-color: white;
            border-radius: 8px;
            padding: 30px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            margin-bottom: 30px;
            text-align: center;
        }

        .file-input-wrapper {
            position: relative;
            display: inline-block;
            margin-bottom: 20px;
        }

        .file-input {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            cursor: pointer;
        }

        .file-input-label {
            display: inline-block;
            padding: 12px 24px;
            background-color: var(--primary-color);
            color: white;
            border-radius: 6px;
            font-weight: 500;
            transition: background-color 0.3s;
            cursor: pointer;
        }

        .file-input-label:hover {
            background-color: #3a5a8c;
        }

        .file-name {
            margin-top: 10px;
            font-size: 0.9rem;
            color: var(--secondary-color);
        }

        .viewer-section {
            display: none;
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            margin-bottom: 30px;
        }

        .toolbar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
            flex-wrap: wrap;
            gap: 10px;
        }

        .toolbar-group {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .btn {
            padding: 8px 16px;
            background-color: var(--light-color);
            border: 1px solid var(--border-color);
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s;
            display: inline-flex;
            align-items: center;
            gap: 6px;
        }

        .btn:hover {
            background-color: #e9ecef;
        }

        .btn-primary {
            background-color: var(--primary-color);
            color: white;
            border-color: var(--primary-color);
        }

        .btn-primary:hover {
            background-color: #3a5a8c;
            border-color: #3a5a8c;
        }

        .btn-danger {
            background-color: #dc3545;
            color: white;
            border-color: #dc3545;
        }

        .btn-danger:hover {
            background-color: #bb2d3b;
            border-color: #bb2d3b;
        }

        .page-controls {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .page-info {
            font-weight: 500;
        }

        .zoom-controls {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .zoom-level {
            min-width: 40px;
            text-align: center;
        }

        .canvas-container {
            width: 100%;
            overflow-x: auto;
            text-align: center;
            margin-bottom: 20px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            padding: 10px;
            background-color: #f0f0f0;
        }

        #pdf-canvas {
            max-width: 100%;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        .view-mode-selector {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        .view-mode-btn {
            padding: 8px 16px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s;
        }

        .view-mode-btn.active {
            background-color: var(--primary-color);
            color: white;
            border-color: var(--primary-color);
        }

        #all-pages-container {
            display: none;
            flex-direction: column;
            gap: 20px;
        }

        .page-canvas {
            width: 100%;
            margin-bottom: 20px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
            border: 1px solid var(--border-color);
        }

        footer {
            text-align: center;
            margin-top: 40px;
            padding-top: 20px;
            border-top: 1px solid var(--border-color);
            color: var(--secondary-color);
            font-size: 0.9rem;
        }

        @media (max-width: 768px) {
            .toolbar {
                flex-direction: column;
                align-items: stretch;
            }

            .toolbar-group {
                justify-content: space-between;
                margin-bottom: 10px;
            }

            .page-controls {
                order: -1;
                margin-bottom: 10px;
            }
        }
JavaScript makes the page work functionally. At last, create a JavaScript file with the name of script.js, and remember that you've got to make a file with a .js extension.


// Set current year in footer
        document.getElementById('year').textContent = new Date().getFullYear();

        // Initialize PDF.js
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js';

        // DOM elements
        const fileInput = document.getElementById('file-input');
        const fileName = document.getElementById('file-name');
        const viewerSection = document.getElementById('viewer-section');
        const pdfCanvas = document.getElementById('pdf-canvas');
        const allPagesContainer = document.getElementById('all-pages-container');
        const pageInfo = document.getElementById('page-info');
        const zoomLevel = document.getElementById('zoom-level');
        const prevPageBtn = document.getElementById('prev-page');
        const nextPageBtn = document.getElementById('next-page');
        const zoomInBtn = document.getElementById('zoom-in');
        const zoomOutBtn = document.getElementById('zoom-out');
        const printBtn = document.getElementById('print-btn');
        const saveImagesBtn = document.getElementById('save-images-btn');
        const closeBtn = document.getElementById('close-btn');
        const singlePageBtn = document.getElementById('single-page-btn');
        const allPagesBtn = document.getElementById('all-pages-btn');
        const singlePageContainer = document.getElementById('single-page-container');

        // PDF variables
        let pdfDoc = null;
        let pageNum = 1;
        let pageRendering = false;
        let pageNumPending = null;
        let scale = 1.0;
        const scaleStep = 0.25;
        const minScale = 0.5;
        const maxScale = 3.0;

        // File input handler
        fileInput.addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (file) {
                fileName.textContent = file.name;
                loadPDF(file);
            }
        });

        // Close PDF handler
        closeBtn.addEventListener('click', function() {
            viewerSection.style.display = 'none';
            fileInput.value = '';
            fileName.textContent = 'No file selected';
        });

        // Load PDF file
        function loadPDF(file) {
            const fileReader = new FileReader();
            
            fileReader.onload = function() {
                const typedArray = new Uint8Array(this.result);
                
                // Load the PDF
                pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
                    pdfDoc = pdf;
                    pageNum = 1;
                    scale = 1.0;
                    
                    // Show the viewer section
                    viewerSection.style.display = 'block';
                    
                    // Reset view mode to single page
                    singlePageBtn.classList.add('active');
                    allPagesBtn.classList.remove('active');
                    singlePageContainer.style.display = 'block';
                    allPagesContainer.style.display = 'none';
                    
                    // Render the first page
                    renderPage(pageNum);
                    
                    // Update page info
                    updatePageInfo();
                    
                    // Render all pages for "All Pages" view
                    renderAllPages();
                }).catch(function(error) {
                    alert('Error loading PDF: ' + error.message);
                });
            };
            
            fileReader.readAsArrayBuffer(file);
        }

        // Render a page
        function renderPage(num) {
            pageRendering = true;
            
            pdfDoc.getPage(num).then(function(page) {
                const viewport = page.getViewport({ scale: scale });
                const canvas = pdfCanvas;
                const context = canvas.getContext('2d');
                
                canvas.height = viewport.height;
                canvas.width = viewport.width;
                
                const renderContext = {
                    canvasContext: context,
                    viewport: viewport
                };
                
                const renderTask = page.render(renderContext);
                
                renderTask.promise.then(function() {
                    pageRendering = false;
                    if (pageNumPending !== null) {
                        renderPage(pageNumPending);
                        pageNumPending = null;
                    }
                });
            });
            
            updatePageInfo();
        }

        // Render all pages for "All Pages" view
        function renderAllPages() {
            allPagesContainer.innerHTML = '';
            
            for (let i = 1; i <= pdfDoc.numPages; i++) {
                const pageDiv = document.createElement('div');
                pageDiv.className = 'page-wrapper';
                pageDiv.innerHTML = `

Page ${i}

`; const canvas = document.createElement('canvas'); canvas.className = 'page-canvas'; pageDiv.appendChild(canvas); allPagesContainer.appendChild(pageDiv); pdfDoc.getPage(i).then(function(page) { const viewport = page.getViewport({ scale: 1.0 }); canvas.height = viewport.height; canvas.width = viewport.width; const renderContext = { canvasContext: canvas.getContext('2d'), viewport: viewport }; page.render(renderContext); }); } } // Queue rendering of a page function queueRenderPage(num) { if (pageRendering) { pageNumPending = num; } else { renderPage(num); } } // Update page info display function updatePageInfo() { pageInfo.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; zoomLevel.textContent = `${Math.round(scale * 100)}%`; } // Previous page button prevPageBtn.addEventListener('click', function() { if (pageNum <= 1) return; pageNum--; queueRenderPage(pageNum); }); // Next page button nextPageBtn.addEventListener('click', function() { if (pageNum >= pdfDoc.numPages) return; pageNum++; queueRenderPage(pageNum); }); // Zoom in button zoomInBtn.addEventListener('click', function() { if (scale >= maxScale) return; scale += scaleStep; queueRenderPage(pageNum); }); // Zoom out button zoomOutBtn.addEventListener('click', function() { if (scale <= minScale) return; scale -= scaleStep; queueRenderPage(pageNum); }); // Print button printBtn.addEventListener('click', function () { if (!pdfDoc) return; pdfDoc.getPage(pageNum).then(function (page) { const viewport = page.getViewport({ scale: scale }); const printCanvas = document.createElement('canvas'); const context = printCanvas.getContext('2d'); printCanvas.width = viewport.width; printCanvas.height = viewport.height; page.render({ canvasContext: context, viewport: viewport }).promise.then(function () { // Open a new window for printing const dataUrl = printCanvas.toDataURL(); const printWindow = window.open('', '_blank'); printWindow.document.write(` Print PDF Page `); printWindow.document.close(); }); }); }); // Save as images button saveImagesBtn.addEventListener('click', async function() { if (!pdfDoc) return; saveImagesBtn.disabled = true; saveImagesBtn.innerHTML = ' Processing...'; try { const zip = new JSZip(); const imgFolder = zip.folder("pdf_images"); // Capture all pages for (let i = 1; i <= pdfDoc.numPages; i++) { const page = await pdfDoc.getPage(i); const viewport = page.getViewport({ scale: 2.0 }); // Higher scale for better quality const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; await page.render({ canvasContext: context, viewport: viewport }).promise; // Convert canvas to blob and add to zip const blob = await new Promise(resolve => { canvas.toBlob(resolve, 'image/jpeg', 0.9); }); imgFolder.file(`page_${i}.jpg`, blob); } // Generate zip file const content = await zip.generateAsync({ type: 'blob' }); saveAs(content, `${fileName.textContent.replace('.pdf', '')}_images.zip`); } catch (error) { alert('Error generating images: ' + error.message); console.error(error); } finally { saveImagesBtn.disabled = false; saveImagesBtn.innerHTML = ' Save as Images (ZIP)'; } }); // View mode buttons singlePageBtn.addEventListener('click', function() { singlePageBtn.classList.add('active'); allPagesBtn.classList.remove('active'); singlePageContainer.style.display = 'block'; allPagesContainer.style.display = 'none'; }); allPagesBtn.addEventListener('click', function() { allPagesBtn.classList.add('active'); singlePageBtn.classList.remove('active'); singlePageContainer.style.display = 'none'; allPagesContainer.style.display = 'flex'; }); // Keyboard navigation document.addEventListener('keydown', function(e) { if (!viewerSection.style.display || viewerSection.style.display === 'none') return; if (e.key === 'ArrowLeft' || e.key === 'PageUp') { if (pageNum > 1) { pageNum--; queueRenderPage(pageNum); e.preventDefault(); } } else if (e.key === 'ArrowRight' || e.key === 'PageDown') { if (pageNum < pdfDoc.numPages) { pageNum++; queueRenderPage(pageNum); e.preventDefault(); } } else if (e.key === '+' || e.key === '=') { if (scale < maxScale) { scale += scaleStep; queueRenderPage(pageNum); e.preventDefault(); } } else if (e.key === '-' || e.key === '_') { if (scale > minScale) { scale -= scaleStep; queueRenderPage(pageNum); e.preventDefault(); } } }); function updatePageInfo() { pageInfo.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; zoomLevel.textContent = `${Math.round(scale * 100)}%`; } // Button event handlers prevPageBtn.addEventListener('click', function() { if (pageNum <= 1) return; pageNum--; queueRenderPage(pageNum); }); nextPageBtn.addEventListener('click', function() { if (pageNum >= pdfDoc.numPages) return; pageNum++; queueRenderPage(pageNum); }); zoomInBtn.addEventListener('click', function() { if (scale >= maxScale) return; scale += scaleStep; queueRenderPage(pageNum); zoomLevel.textContent = `${Math.round(scale * 100)}%`; }); zoomOutBtn.addEventListener('click', function() { if (scale <= minScale) return; scale -= scaleStep; queueRenderPage(pageNum); zoomLevel.textContent = `${Math.round(scale * 100)}%`; });
We hope you would like this Calculator using html css and javascript code

Thank you
Learning robo team

Post a Comment

Thank you
Learning robo team

Post a Comment (0)

Previous Post Next Post
Learning Robo says...
code copied
Welcome